paint-brush
Изменяемые ссылки или изменяемые переменные? Расшифровка мут-мут-мут в Rustк@charnog
904 чтения
904 чтения

Изменяемые ссылки или изменяемые переменные? Расшифровка мут-мут-мут в Rust

к Denis Gonchar5m2023/09/23
Read on Terminal Reader
Read this story w/o Javascript

Слишком долго; Читать

Безопасность памяти в Rust основана на механизмах владения и заимствования. В этой статье рассматриваются тонкости ключевого слова mut, позволяющие различать изменяемые ссылки и изменяемые переменные. Ключевой вывод? Не все мутации одинаковы: изменяемые ссылки позволяют изменять значение, на которое указывает, тогда как изменяемые переменные со ссылками могут изменять то место, куда они указывают. Удобный прием визуализации с использованием знака = упрощает понимание этих различий.
featured image - Изменяемые ссылки или изменяемые переменные? Расшифровка мут-мут-мут в Rust
Denis Gonchar HackerNoon profile picture

вступление

Несмотря на то, что Rust предоставляет свою книгу по Rust, мне было сложно понять ключевое слово mut . Я спросил себя: «Я единственный, у кого такая проблема?» Быстрый поиск в Google подтвердил, что я не одинок.


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

Переменные

Чтобы создать неизменяемую переменную в Rust, просто напишите let x = 1337 . Это просто. Если вы хотите создать переменную, которую можно будет изменить позже, просто добавьте ключевое слово mut после let . В Rust есть полезное соглашение, которое способствует ясности намерений.


Добавление ключевого слова mut сообщает другим, что эта переменная будет изменена где-то в другом месте кода. Хорошо.


Давайте визуализируем это. Здесь две переменные, let mut x = 1337 и let y = 42 .

Имя; второй - тип, третий - значение, четвертый - флаг mut.

Ссылки

На данный момент все просто. Однако при использовании ссылок 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 , верно? Опишем их.


  1. let mut x_ref : я создаю изменяемую переменную с именем x_ref , что означает, что я могу изменить ее значение позже.


  2. &mut i32 : Я утверждаю, что переменная будет содержать изменяемые ссылки на некоторое значение типа i32 .


  3. &mut x : я заимствую (получаю ссылку) переменную x .


Затем я создал переменную с именем z и присвоил ей значение 0 . Позже, когда я написал x_ref = &mut z , я указал, что понимаю x_ref как изменяемую переменную, которая может содержать только ссылки на значения i32 .


Поскольку тип zi32 , я могу присвоить его адрес переменной 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. Эта простая визуализация может прояснить многие недоразумения.


Приятного кодирования!