В чем разница между `mut` перед именем переменной и после двоеточия в Rust?
Основное различие: мутабельность переменной vs. мутабельность ссылки
Вы столкнулись с классической путаницей начинающих Rust-разработчиков. Ключевое слово mut действительно может появляться в двух разных контекстах, и это принципиально разные вещи. Давайте разберем их по порядку.
1. mut перед именем переменной — мутабельность переменной
Когда вы пишете:
let mut x = 5;
Вы объявляете, что сама переменная x является изменяемой. Это означает, что вы можете переприсваивать ей значения и изменять ее содержимое. Без mut переменная по умолчанию неизменяема (иммутабельна).
Правильный пример:
let mut x = 5;
x += 10; // Работает, потому что x объявлена как mut
println!("{}", x); // 15
let y = 5;
y += 10; // Ошибка! y неизменяема
2. mut после двоеточия — мутабельная ссылка
Конструкция : mut i32, которую вы попробовали, не является допустимой в объявлении переменной. Rust не позволяет так писать. Правильный синтаксис для мутабельных ссылок выглядит иначе:
let x = &mut 5; // мутабельная ссылка на значение 5
// или
let y: &mut i32 = &mut 5; // явное указание типа
Здесь mut стоит после & и перед типом. Это означает, что ссылка является мутабельной — вы можете изменять значение, на которое она указывает.
3. Когда используется mut после двоеточия?
На самом деле, mut после двоеточия встречается в двух случаях:
- В типах ссылок:
&mut T— мутабельная ссылка на тип T. - В типах указателей:
*mut T— сырой мутабельный указатель.
Пример с функциями:
fn change_value(x: &mut i32) {
*x += 10;
}
fn main() {
let mut a = 5;
change_value(&mut a);
println!("{}", a); // 15
}
Здесь x: &mut i32 — это параметр функции, который принимает мутабельную ссылку на i32. Сам параметр x (ссылка) неизменяем, но значение, на которое она указывает, можно менять.
4. Почему ваш код не работает?
Ваш второй пример:
let b: mut i32 = 20; // Ошибка!
Компилятор ожидает после двоеточия тип (например, i32, String, &mut i32), а не модификатор mut. mut — это не тип, а ключевое слово, которое изменяет поведение переменной или ссылки.
Краткая таблица для запоминания
| Синтаксис | Значение | Пример |
|---|---|---|
let mut x = ... |
Переменная x изменяема |
let mut x = 5; x += 1; |
&mut T |
Мутабельная ссылка на тип T | let y = &mut x; |
*mut T |
Сырой мутабельный указатель | let p: *mut i32 = &mut x; |
Вывод: mut перед именем переменной делает саму переменную изменяемой. mut после двоеточия (внутри типа) делает ссылку или указатель мутабельными. Запомните это различие — оно поможет избежать ошибок компиляции и лучше понять систему владения в Rust.
Дополнение: мутабельность в контексте заимствования
Коллега верно объяснил базовое различие. Хочу добавить важный нюанс, который часто упускают новички: mut перед переменной и mut в ссылке — это разные уровни мутабельности, и они могут комбинироваться.
Рассмотрим пример:
let mut x = 10;
let y = &mut x; // y — неизменяемая ссылка на мутабельное значение
*y += 5; // работает, потому что ссылка мутабельная
let mut z = &x; // z — мутабельная переменная, хранящая неизменяемую ссылку
z = &20; // можно перенаправить z на другое значение, но менять *z нельзя
В первом случае (y: &mut x) — сама ссылка y неизменяема (вы не можете сделать y = &mut something_else), но значение, на которое она указывает, можно менять. Во втором случае (mut z) — переменная z изменяема (можно присвоить ей другую ссылку), но сама ссылка неизменяема (нельзя менять *z).
Это различие критически важно при проектировании API и работе с функциями. Если вы видите сигнатуру fn foo(x: &mut i32), это значит, что функция может изменить значение, на которое указывает x, но не может изменить саму ссылку x (она всегда указывает на один и тот же объект).
Совет: всегда думайте о том, что именно вы хотите сделать — изменить содержимое данных или перенаправить указатель. Это поможет правильно расставить mut в коде.