ProgrammingRust backend developer

Explain how work with immutable and mutable data structures is arranged in Rust, and when mutability is necessary. What are the practical limitations associated with this, and how can they be skillfully bypassed?

Pass interviews with Hintsage AI assistant

Answer.

In Rust, data structures are immutable by default. This means that any variable or reference declared without the mut keyword cannot be changed after initialization.

let mut value = 10; // mutable variable value += 5; let value2 = 10; // immutable variable // value2 += 5; // compilation error: cannot assign twice to immutable variable

The same logic applies to struct fields:

struct Point { x: i32, y: i32 } let mut pt = Point { x: 1, y: 2 }; pt.x = 5; // OK, since pt is declared as mut

However, mutability only affects the "top level" of the variable: if a structure is stored behind an immutable reference, its data cannot be changed even if declared as mut within the structure.

Another way to bypass limitations is to use special types like RefCell<T> or atomic containers. This allows changing data inside seemingly immutable containers through "interior mutability", for example:

use std::cell::RefCell; struct Counter { count: RefCell<i32>, } let counter = Counter { count: RefCell::new(0) }; *counter.count.borrow_mut() += 1; // safe, despite the absence of mut

Tricky question.

Question: Can you change the value of a struct field if the variable itself is not declared as mut, but the field is explicitly declared as mut in the struct?

Typical incorrect answer: Yes, if the field is declared as mut, it can be changed.

Correct answer: The mut keyword cannot be used when declaring a struct field. Mutability only refers to the variable itself or the obtained reference. To modify a struct field, you must either declare the variable as mut or use types of "interior mutability" (for example, RefCell).

Example:

struct Foo { val: i32 } // field declared without mut let mut foo = Foo { val: 1 }; foo.val = 2; // OK let foo2 = Foo { val: 3 }; foo2.val = 4; // error! variable is not mutable

Examples of real mistakes due to ignorance of the topic.


Story

In a large project, one of the developers tried to change a struct field, thinking that declaring the field with mut ("struct S { mut x: i32 }") would provide the needed mutability. This could not compile, and the refactoring process dragged on for several hours until it was explained to him that mutability is a feature of the variable's ownership, not the structure.


Story

In a multithreading-related project, all logic for controlling data access was held in a global static structure. Not knowing about the possibilities of "interior mutability" via RefCell, the team wrote overly complex locking and racing mechanisms instead of a simple solution using Arc<Mutex<T>> or RwLock<T>.


Story

A developer forgot to declare a function argument as mut in the signature, and the function could not modify the passed structure, which caused a bug to manifest only in production (data was not updated after the function call). Knowing that by default passing goes by immutable reference would have allowed him to avoid the absence of an error at early stages.