Антон Долганин

Я инженер, который решает задачи, а не пишет на языке. Архитектура, разработка, DevOps — подбираю инструменты под цель, строю решения, которые работают в проде и масштабируются без боли.

Статья не предполагает, что вы узнаете что-то новое из Rust, или вы научитесь ему. Это скорее эксперимент показать ребенку как он выглядит изнутри (Rust, не ребенок), а не как на нем надо писать.

То есть, еще раз, разработчики привыкли смотреть на язык с позиции как надо им, и если язык не укладывается в их парадигму, это начинает бесить.

Тут я постарался посмотреть на ситуацию с точки зрения "книжки с картинками", как язык живет, а не как мы привыкли.

⚠️ Дисклеймер — в статье речь не про Copy-тип, к которым относятся, например, i32, f64, bool, char, &T. А про move-типы (например, String), которые перемещают значения.

Ну, попробуем.

Отталкиваемся от того, что в Rust значением (ячейкой памяти) может владеть только одна переменная. Введем обозначения: let — "позволим", = — "владеть", и еще mut — кеш, наличка, деньги.


let

Позволим a владеть House:


let a = String::from("House");

Позволим b владеть этим House. По всем правилам, предыдущий владелец уходит из дома.


let b = a;

Теперь:


println!("a = {}", a);

Будет ошибочка, a теперь не имеет прав что-либо делать со значением (своим House), так он его отдал.

mut

Но и это еще не все, владеть это лишь как арендовать чужой House. Вы не можете его изменять.

Тут снова будет ошибка:


b.push_str(" with Pool"); // 🔴 ошибка

Если вы хотите его менять, вы должны купить House, за валюту mut:


let mut c = b;
c.push_str(" with pool");
println!("{}", c); // House with pool

&

Мы можем разделить владение House, например, пустив жить приятеля. Ты передаешь приятелю связку ключей (&):


let d = &c;
println!("{}", c);
println!("{}", d);

Но есть нюанс. По правилам приличия, если пустили гостей, нельзя делать ремонт. Следующий код даст ошибку:


let d = &c;
c.push_str(" with sandbox");// если пустили гостей, нельзя делать ремонт
println!("{}", c);
println!("{}", d);// вот гость

Можно дождаться пока гости уйдут:


let d = &c;
println!("{}", d);// тут гость вышел
c.push_str(" with sandbox");

Непонятно? В самом конце статьи есть пояснение про гостей.

& mut

Но тут приятель захотел еще джакузи. Так как это вносит изменение в House, ты просишь оплатить часть House и тогда он может менять твой House тоже. Ты передаешь ему ключи в обмен на деньги (&mut).


let e = &mut c;
e.push_str(" and with hot tub");
println!("{}", e);

Не забывайте, что правило действует то же — при гостях ты уже не можешь делать ремонт. Более того! Пока твой приятель сидит в джакузи, ты не можешь даже зайти в дом:


let e = &mut c;
e.push_str(" and with hot tub");
println!("{}", c);
println!("{}", e);// приятель в джакузи 

Надо дождаться, пока он выйдет:


let e = &mut c;
e.push_str(" and with hot tub");
println!("{}", e);// приятель вышел
println!("{}", c);

Это именно когда мы дали аренду за деньги &mut, а не пустили пожить бесплатно (&).

mut &mut

Сдаем в аренду другому приятелю House с правом изменения. Но приятель обладает еще своим кешем (деньгами) mut:


let mut f = &mut c;
f.push_str(" and with garden");

Дела идут в гору, мы покупаем Apartment:


let mut c2 = String::from("Apartment");

Приятель не отстает — дай, говорит, еще и Apartment погонять, кеш у меня есть (let mut f). Он съезжает с House, заезжает в Apartment, меняет и ее:


f = &mut c2;
f.push_str(" with elevator");

Принцип тот же — ключи на деньги — &mut. А инициализация let mut f нам позволяет не привязываться к одному месту (менять его). Другими словами, это про разное.

Когда приятель ушел, мы имеем по факту и измененный House и Apartment:


println!("c: {}", c);
println!("c2: {}", c2);
//c: House with pool and with sandbox and with hot tub and with garden
//c2: Apartment with elevator

Заметьте, под "приятель ушел" я имею в виду буквально "ушел", его нет, все. Rust следит за этим, и, если далее по коду переменная не применяется, он разрешает продолжить работу с вашей собственностью. Поэтому в примерах выше было ломающее мозг поведение — пока гость дома, нельзя что-то делать, но нет явных признаков, что он ушел. Переменная не применяется далее по коду — Rust считает ее "ушедшей", как-будто применили к ней delete.

Функции

За редкими исключениями, принцип тот же, не буду здесь ими грузить. Может быть в другой раз :)

Rust для ребятишек