ПрограммированиеRust backend разработчик

Объясните, как устроена работа с неизменяемыми (immutable) и изменяемыми (mutable) структурами данных в Rust, и когда необходима мутабельность? Каковы практические ограничения, связанные с этим, и как их можно грамотно обходить?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Rust структуры данных по умолчанию неизменяемы. Это означает, что любая переменная или ссылка, объявленная без ключевого слова mut, не может быть изменена после инициализации.

let mut value = 10; // изменяемая переменная value += 5; let value2 = 10; // неизменяемая переменная // value2 += 5; // ошибка компиляции: cannot assign twice to immutable variable

Та же логика распространяется на поля структур:

struct Point { x: i32, y: i32 } let mut pt = Point { x: 1, y: 2 }; pt.x = 5; // OK, т.к. pt объявлен как mut

Однако мутабельность затрагивает только "верхний уровень" переменной: если структура хранится за неизменяемой ссылкой, то её данные нельзя изменять, даже если они объявлены с mut внутри структуры.

Другой способ обойти ограничения — использовать специальные типы вроде RefCell<T> или атомарные контейнеры. Это позволяет изменять данные внутри неизменяемых по виду контейнеров при помощи "внутренней мутабельности" (interior mutability), например:

use std::cell::RefCell; struct Counter { count: RefCell<i32>, } let counter = Counter { count: RefCell::new(0) }; *counter.count.borrow_mut() += 1; // безопасно, несмотря на отсутствие mut

Вопрос с подвохом.

Вопрос: Можно ли изменить значение поля структуры, если сама переменная не объявлена с mut, но поле явно объявлено как mut в самой структуре?

Типичный неверный ответ: Да, если поле объявлено с mut, его можно менять.

Правильный ответ: Ключевое слово mut не может быть использовано при объявлении поля структуры. Мутабельность относится только к самой переменной или к полученной ссылке. Чтобы модифицировать поле структуры, нужно либо объявить переменную как mut, либо использовать типы "внутренней мутабельности" (например, RefCell).

Пример:

struct Foo { val: i32 } // поле объявляется без mut let mut foo = Foo { val: 1 }; foo.val = 2; // OK let foo2 = Foo { val: 3 }; foo2.val = 4; // ошибка! переменная не mutable

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В крупном проекте один из разработчиков попытался изменить поле структуры, считая, что объявление поля с mut ("struct S { mut x: i32 }") даст нужную мутабельность. Это скомпилироваться не смогло, и процесс рефакторинга затянулся на несколько часов, пока ему не объяснили, что мутабельность — характеристика владения переменной, а не структуры.


История

В проекте, связанное с многопоточностью, вся логика контроля доступа к данным держалась в глобальной статической структуре. Не зная о возможностях "внутренней мутабельности" через RefCell, команда писала избыточно сложные механизмы блокировок и гонок, вместо простого решения через Arc<Mutex<T>> или RwLock<T>.


История

Девелопер забыл объявить аргумент функции mut внутри сигнатуры, и функция не смогла изменить переданную структуру, из-за чего баг проявился только в проде (данные не обновлялись после вызова функции). Знание, что по умолчанию передача идёт по неизменяемой ссылке, позволило бы ему отделаться отсутствием ошибки на ранних этапах.