Appendix D — Rust Interview Toolkit

The Rust examples form one standard-library-only Cargo package. They use ownership explicitly but keep types and iterator chains small enough to reproduce under interview pressure.

D.1 Slices and vectors

Prefer a borrowed slice when the algorithm only reads input:

pub fn search(nums: &[i32], target: i32) -> Option<usize>
API Interview use
nums.len() item count
nums.iter() borrow each item
nums.to_vec() make an owned copy
values.push(x) append to a Vec
vec![value; n] initialized vector

Indices are usize. Half-open intervals such as [low, high) often avoid subtracting from zero.

D.2 Option and Result

Option<T> expresses a value that may be absent without a sentinel:

if let Some(&earlier_index) = index_by_value.get(&needed) {
    return Some([earlier_index, index]);
}

Use Result<T, E> when construction can fail and the reason matters, as in validated intervals. In interview code, ?, if let, let ... else, and match cover most control flow.

D.3 Hash maps and queues

std::collections::HashMap provides expected \(O(1)\) lookup and insertion. entry is useful for grouping and counting:

groups.entry(key).or_insert_with(Vec::new).push(word);
*frequency.entry(word).or_insert(0) += 1;

VecDeque is the standard FIFO queue. Use push_back, pop_front, and len for breadth-first traversal.

D.4 Sorting and ordering

slice.sort() uses natural order. sort_by_key works when a key can be produced cheaply; sort_by handles borrowed data and mixed directions:

ranked.sort_by(|left, right| {
    frequency[right]
        .cmp(&frequency[left])
        .then_with(|| left.cmp(right))
});

For floating-point endpoints, f64::total_cmp supplies a total ordering. Validate away non-finite values when the problem contract requires finite endpoints.

D.5 Strings and characters

str is UTF-8 and cannot be indexed by an integer. Use text.chars() for Unicode scalar values or text.bytes() when the contract is explicitly ASCII:

for (index, character) in text.chars().enumerate() {
    // character index, not byte offset
}

Clarify whether the problem expects bytes, scalar values, or user-perceived grapheme clusters.

D.6 Owned linked nodes

An acyclic owned link is conventionally:

type Link = Option<Box<ListNode>>;

take() detaches an owned link before rewiring it. This makes node reuse visible to the compiler and avoids copying data nodes.

Cycles require shared ownership. The cycle example uses Rc<RefCell<ListNode>>: Rc provides multiple owners, RefCell permits checked interior mutation while building fixtures, and Rc::ptr_eq compares identity. Use this heavier representation only when the structure truly needs shared or cyclic links.

D.7 Borrowed trees

Option<Box<TreeNode>> owns child nodes. Traversals can borrow through as_deref(), placing &TreeNode references in a queue or returning a reference tied to the tree’s lifetime. This avoids cloning or moving the tree.

D.8 Cargo tests

Tests live beside each solution under #[cfg(test)]:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ordinary_case() {
        assert_eq!(expected, function(input));
    }
}

From book/src/rust, run every test with cargo test or filter one module with cargo test two_sum_solution.