Avatar ·

В чем разница между `mut` перед именем переменной и после двоеточия в Rust?

Всем привет! Я начал изучать Rust недавно и столкнулся с путаницей в синтаксисе объявления переменных. В учебниках я вижу два разных способа использования ключевого слова `mut`, и не могу понять, в чем между ними разница. Первый способ — `mut` ставится перед именем переменной: ```rust let mut x = 5; ``` Второй способ — `mut` ставится после двоеточия, когда указывается тип: ```rust let x: mut i32 = 5; ``` Я попробовал оба варианта в своем коде, но второй вариант вызывает ошибку компиляции. Вот пример, который я написал: ```rust fn main() { let mut a = 10; // работает a += 5; let b: mut i32 = 20; // ошибка! b += 10; println!("a = {}, b = {}", a, b); } ``` Компилятор говорит что-то про "expected type, found keyword mut". Я думал, что `mut` можно ставить в любом месте, но, видимо, это не так. Объясните, пожалуйста, какая между этими вариантами разница и почему второй не работает? Может быть, есть случаи, когда `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 в коде.