В 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 внутри сигнатуры, и функция не смогла изменить переданную структуру, из-за чего баг проявился только в проде (данные не обновлялись после вызова функции). Знание, что по умолчанию передача идёт по неизменяемой ссылке, позволило бы ему отделаться отсутствием ошибки на ранних этапах.