Перейти к основному содержимому
Модуль 3 · Урок 1 из 25/14 в курсе~15 min
Уроки модуля (1/2)

Правила владения

Право собственности — самая уникальная особенность Rust. Это позволяет языку гарантировать безопасность памяти без необходимости использования сборщика мусора или необходимости вручную освобождать память кучи (как в C или C++).

Три правила владения

Управление памятью в Rust регулируется тремя простыми правилами, строго проверяемыми компилятором:

  1. Каждое значение в Rust имеет переменную, называемую его владельцем.
  2. Одновременно может быть только один владелец.
  3. Когда владелец выходит за пределы области действия, значение автоматически удаляется.

Стек против кучи

Чтобы понять право собственности, важно знать, где находятся данные:

  • Стек: хранит данные фиксированного размера, известные во время компиляции (например, целые числа, логические значения). Доступ очень быстрый.
  • Куча: хранит данные динамического размера, неизвестные во время компиляции (например, String). Выделение происходит медленнее и требует наличия указателя, хранящегося в стеке.

Понятие «Переезд».

Когда мы присваиваем одну переменную, выделенную в куче, другой, Rust перемещает (или передает) право собственности на значение, делая первую переменную недействительной:

Rust
let s1 = String::from("ciao");
let s2 = s1; // Ownership of the text moves to s2. s1 is NO longer usable!

// println!("{}", s1); // COMPILATION ERROR! s1 is "borrow of moved value"
println!("{}", s2); // Valid!

Такое поведение позволяет избежать «двойной ошибки освобождения» (попытки освободить одну и ту же память дважды при выходе за пределы области видимости), поскольку только s2 освободит память.

Концепция «Клона» (Deep Copy)

Если нам явно необходимо скопировать все содержимое String (как указатели стека, так и фактические текстовые данные в куче), мы можем использовать метод .clone(). Это полностью дублирует данные в куче, сохраняя действительными как исходную, так и новую переменную, но за счет снижения производительности при новом выделении памяти:

Rust
let s1 = String::from("ciao");
let s2 = s1.clone(); // Deep copy. Both variables remain valid!
println!("s1: {}, s2: {}", s1, s2);

Понятие «копия».

Для простых типов, полностью хранящихся в стеке (например, целые числа i32, логические значения bool, символы char), присваивание выполняет фактическое автоматическое поверхностное копирование, то есть обе переменные остаются действительными:

Rust
let x = 5;
let y = x; // Copies the value 5 onto the stack. Both variables are usable!
println!("x: {}, y: {}", x, y); // Valid!

Попробуйте сами

Упражнение#rust.m3.l1.e1
Попыток: 0Загрузка…

Объявите строку s1, содержащую текст «привет». Назначьте s1 на s2 (чтобы передать право собственности). Наконец, напечатайте s2, используя println!.

Загрузка редактора…
Показать подсказку

Используйте `let s2 = s1;`, чтобы передать право собственности на `s2` и распечатать его с помощью `println!('{}', s2);`.

Решение доступно после 3 попыток

Упражнение#rust.m3.l1.e2
Попыток: 0Загрузка…

Объявите целочисленную переменную x со значением 5. Присвойте x значению y (выполняя копирование). Наконец, напечатайте x и y в одном и том же println! заявление.

Загрузка редактора…
Показать подсказку

Выполните `let y = x;`, а затем напечатайте оба значения, например `println!('{} {}', x, y);`.

Решение доступно после 3 попыток

Упражнение#rust.m3.l1.e3
Попыток: 0Загрузка…

Declare a String s1 with the value "Rust". Create a deep copy of s1 in a new variable s2 using the clone method. Finally, print both s1 and s2 separated by a space using the println! macro.

Загрузка редактора…
Показать подсказку

Use `let s2 = s1.clone();` to deep copy the value of `s1` into `s2`, then print them with `println!("{} {}", s1, s2);`.

Решение доступно после 3 попыток