ProgrammingBackend Developer

Describe the different types of references in Rust: the differences between mutable and immutable references, rules of exclusivity and lifetime, and how to properly write functions that accept different types of references. Provide syntax examples and discuss typical mistakes when working with references.

Pass interviews with Hintsage AI assistant

Answer

In Rust, there are two main types of references:

  • Immutable references (&T): provide read-only access.
  • Mutable references (&mut T): allow modifying the value they point to.

Rules:

  • You can have either any number of immutable references or only one mutable reference at a time; you cannot mix them in the same scope.
  • References have a lifetime that is checked at compile time.

Syntax examples:

fn read(val: &i32) { println!("{}", val); } fn write(val: &mut i32) { *val += 1; } let mut x = 5; read(&x); write(&mut x);

Trick question

Question: Can you create two mutable references to one variable in different scopes of the same function?

Answer: No, even if the references are created in different blocks, the scope for borrow checking covers the entire function or variable unless the compiler can prove that the references do not overlap. This often results in compile errors:

let mut x = 10; let r1 = &mut x; { let r2 = &mut x; // error! }

Examples of real mistakes due to a lack of understanding of the nuances of the topic


Story

In parser development, a developer tried to store both a mutable and an immutable reference to the same buffer for optimization of reading and writing. As a result, the code would not compile, and after "bypassing" the rule through unsafe code, data leakage bugs appeared.


Story

At the start of a data processing project, one of the participants did not "reanalyze borrow checker" for complex code with nested scopes. As a result, due to overlapping references, the classic "cannot borrow as mutable because it's also borrowed as immutable" error arose without an explicit indication of the problem's location in the source code.


Story

In multithreaded code, plain references were used to access shared data. When trying to modify data in parallel threads, unexpected compile errors and data races occurred when bypassing the borrow checker via unsafe code.