A strong answer
Rust's borrow checker enforces one universal law (Aliasing XOR Mutability / RwLock at Compile Time): at any point in execution, a value may have any number of immutable shared references (&T) OR at most ONE mutable exclusive reference (&mut T), never both simultaneously, and no reference may outlive its referent (Lifetimes 'a). In let first = &vec[0]; vec.push(42); println!("{first}");, first holds an immutable borrow &vec, vec.push(42) requires a mutable borrow &mut vec, and println! uses first afterward—so the borrow checker rejects it before compilation finishes! Under Non-Lexical Lifetimes (NLL), a borrow's lifetime ends at the last instruction that uses that reference (not the closing } brace of the lexical scope), so if first is not used after vec.push(42), the code compiles cleanly! Furthermore, slice references &[T] and &str are 16-byte Fat Pointers (ptr: *const T, len: usize) that can view any contiguous range—heap Vec, stack array [T; N], or static binary .rodata (&'static str)—with one uniform type!
At any statement $S$, if a reference r is live (will be read or dereferenced at or after $S$), then: (1) the referent owned variable cannot be moved or dropped at $S$, and (2) if r is &T, no &mut T to an overlapping path exists at $S$; if r is &mut T, no other &T or &mut T to an overlapping path exists at $S$. Because vec.push(x) requires &mut vec, the Aliasing-XOR-Mutability rule statically guarantees that no live &T pointer into vec's backing buffer exists when push runs—making pointer invalidation from reallocation impossible in safe Rust.
All borrow and lifetime ('a) checks are 100% erased at compile time (0 ns runtime cost, 0 extra bytes in memory), and &mut T allows the LLVM backend to emit noalias metadata (equivalent to C's restrict) on every mutable slice, unlocking aggressive register caching and SIMD vectorization! In production systems (Rust compiler MIR borrowck (Polonius / NLL), LLVM noalias optimization passes, zero-copy parsers (nom, winnow, serde zero-copy &'de str deserialization), and slice::split_at_mut.), engineers monitor these exact trade-offs to keep p99 latency predictable and prevent resource exhaustion under peak load.
What the interviewer is listening for
- Traces how the Borrow Checker (
&TXOR&mut T), Non-Lexical Lifetimes (NLL), and 16-byte fat pointers operates in memory step by step rather than giving a vague textbook definition. - States the core correctness invariant and explains why edge cases do not violate it.
- Quantifies time, space, and hardware costs (Lifetimes
'a& Borrow Checks, 16-Byte Fat Pointer (&str,&[T])) with concrete numbers. - Identifies real-world failure modes (Writing a helper method
fn calc(&self) -> i32when callers inside&mut selfalready hold&mut self.items) and explains how production systems mitigate them.
Common mistakes
- Treating the Borrow Checker (
&TXOR&mut T), Non-Lexical Lifetimes (NLL), and 16-byte fat pointers as a black box without explaining the underlying memory or state transitions. - Ignoring worst-case tail latency or memory overhead (Trying to get two mutable references
&mut slice[0]and&mut slice[1]by indexingslicetwice). - Forgetting boundary conditions and error recovery (Annotating structs with unnecessary lifetime parameters
struct Config<'a> { name: &'a str }for long-lived application state).