Несмотря на то, что Rust предоставляет свою книгу по Rust, мне было сложно понять ключевое слово mut
. Я спросил себя: «Я единственный, у кого такая проблема?» Быстрый поиск в Google подтвердил, что я не одинок.
В результате я решил написать эту статью, чтобы дать подробное объяснение ключевого слова mut
. Эта статья предназначена для тех, кто знаком с языками высокого уровня, такими как Python или JavaScript.
Чтобы создать неизменяемую переменную в Rust, просто напишите let x = 1337
. Это просто. Если вы хотите создать переменную, которую можно будет изменить позже, просто добавьте ключевое слово mut
после let
. В Rust есть полезное соглашение, которое способствует ясности намерений.
Добавление ключевого слова mut
сообщает другим, что эта переменная будет изменена где-то в другом месте кода. Хорошо.
Давайте визуализируем это. Здесь две переменные, let mut x = 1337
и let y = 42
.
На данный момент все просто. Однако при использовании ссылок mut
все становится немного запутанным. Давайте создадим некоторые.
let mut x = 1337; let y = 42; let x_ref = &mut x; let y_ref = &y;
Я создал две отсылки (или «позаимствовал» в терминах Rust). Одна из них — изменяемая ссылка, а другая — ссылка только для чтения. Давайте создадим для этого схему еще раз.
В данной схеме у меня 4 переменные, 2 из которых являются ссылками. Обе ссылочные переменные являются неизменяемыми и не имеют ключевого слова mut
после let
, что означает, что я не могу изменить то, на что они указывают. Однако я все еще могу изменить значение, на которое они ссылаются.
*x_ref = 777;
Если вы напишете это, компилятор Rust не будет жаловаться, а значение x
(а не сама ссылка) изменится на 777
. Однако на схеме есть красный квадрат, указывающий, что x_ref
отсутствует опция изменчивости. Итак, почему я могу изменить значение, на которое оно ссылается?
Вернемся к схеме let x_ref = &mut x
.
Первый белый блок содержит имя: x_ref
. Второй сообщает мне о типе, хранящемся в этой переменной. В полной форме, без каких-либо неявных аннотаций типов, я могу написать следующее:
let x_ref: &mut i32 = &mut x;
Я могу интерпретировать это так: давайте создадим неизменяемую переменную с именем x_ref
, которая будет содержать изменяемую ссылку на i32
, и немедленно инициализируем ее изменяемой ссылкой на значение i32
в переменной x
.
Это означает, что я могу изменить значение, на которое оно указывает, но не могу изменить значение (или адрес) ссылки. Другими словами, я не могу написать что-то вроде:
let x_ref: &mut i32 = &mut x; let mut z = 0; x_ref = &mut z; // Not allowed!
Что касается схем, я хочу изменить направление, на которое указывает стрелка в блоке кода выше. Однако, даже если переменная z
является изменяемой, я не могу изменить стрелку, поскольку проблема заключается в неизменяемости самого x_ref
.
Чтобы изменить направление стрелки, мне нужно изменить адрес, хранящийся в переменной x_ref
. Однако я не могу этого сделать, поскольку переменная неизменяема.
Давай сделаем это!
let mut x: i32 = 1337; let mut x_ref: &mut i32 = &mut x; // I've added mut before x_ref let mut z = 0; x_ref = &mut z; // Allowed!
Здесь, вокруг x_ref
, слишком много случаев mut
, верно? Опишем их.
let mut x_ref
: я создаю изменяемую переменную с именем x_ref
, что означает, что я могу изменить ее значение позже.
&mut i32
: Я утверждаю, что переменная будет содержать изменяемые ссылки на некоторое значение типа i32
.
&mut x
: я заимствую (получаю ссылку) переменную x
.
Затем я создал переменную с именем z
и присвоил ей значение 0
. Позже, когда я написал x_ref = &mut z
, я указал, что понимаю x_ref
как изменяемую переменную, которая может содержать только ссылки на значения i32
.
Поскольку тип z
— i32
, я могу присвоить его адрес переменной x_ref
. Чтобы получить адрес z
, я использовал синтаксис &mut z
.
Схема.
Взгляните на =
в операторе, это может показаться немного очевидным, но…
let mut x_ref = &mut x;
… Я рассматриваю это как разделитель (особенно если повернуть его на 90 градусов), который делит утверждение на два подпредложения: левое и правое. Левая часть предоставляет информацию о самой переменной , а правая — о значении .
Когда я использую оператор разыменования *
для изменения значения...
*x_ref = 100;
... я не меняю значение переменной x_ref
. Вместо этого я меняю значение, на которое ссылается x_ref
.
Раньше я часто использовал mut
. Что, если я опущу некоторые из них?
let i = 1; let j = 2; let mut k = &i;
Могу ли я изменить значение i
здесь? Используя технику делителя, ответить довольно просто. Я могу изменить значение k
(я вижу mut
слева), но значение (справа) является неизменяемой ссылкой на i
(здесь нет mut
).
Поэтому…
let i = 1; let j = 2; let mut k = &i; k = &j; // This is legal. *k = 3; // This is not.
Схема.
В этой статье мы разобрали нюансы ключевого слова mut
и ссылок. Помните, что существует различие между изменяемой ссылкой и изменяемой переменной, содержащей ссылку. Наша хитрость?
Использование знака =
в качестве мысленного разделителя для лучшего понимания присваиваний в Rust. Эта простая визуализация может прояснить многие недоразумения.
Приятного кодирования!