Уроки модуля (1/2)
Правила владения
Право собственности — самая уникальная особенность Rust. Это позволяет языку гарантировать безопасность памяти без необходимости использования сборщика мусора или необходимости вручную освобождать память кучи (как в C или C++).
Три правила владения
Управление памятью в Rust регулируется тремя простыми правилами, строго проверяемыми компилятором:
- Каждое значение в Rust имеет переменную, называемую его владельцем.
- Одновременно может быть только один владелец.
- Когда владелец выходит за пределы области действия, значение автоматически удаляется.
Стек против кучи
Чтобы понять право собственности, важно знать, где находятся данные:
- Стек: хранит данные фиксированного размера, известные во время компиляции (например, целые числа, логические значения). Доступ очень быстрый.
- Куча: хранит данные динамического размера, неизвестные во время компиляции (например,
String). Выделение происходит медленнее и требует наличия указателя, хранящегося в стеке.
Понятие «Переезд».
Когда мы присваиваем одну переменную, выделенную в куче, другой, 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(). Это полностью дублирует данные в куче, сохраняя действительными как исходную, так и новую переменную, но за счет снижения производительности при новом выделении памяти:
let s1 = String::from("ciao");
let s2 = s1.clone(); // Deep copy. Both variables remain valid!
println!("s1: {}, s2: {}", s1, s2);Понятие «копия».
Для простых типов, полностью хранящихся в стеке (например, целые числа i32, логические значения bool, символы char), присваивание выполняет фактическое автоматическое поверхностное копирование, то есть обе переменные остаются действительными:
let x = 5;
let y = x; // Copies the value 5 onto the stack. Both variables are usable!
println!("x: {}, y: {}", x, y); // Valid!Попробуйте сами
Объявите строку s1, содержащую текст «привет». Назначьте s1 на s2 (чтобы передать право собственности). Наконец, напечатайте s2, используя println!.
Показать подсказку
Используйте `let s2 = s1;`, чтобы передать право собственности на `s2` и распечатать его с помощью `println!('{}', s2);`.
Решение доступно после 3 попыток
Объявите целочисленную переменную x со значением 5. Присвойте x значению y (выполняя копирование). Наконец, напечатайте x и y в одном и том же println! заявление.
Показать подсказку
Выполните `let y = x;`, а затем напечатайте оба значения, например `println!('{} {}', x, y);`.
Решение доступно после 3 попыток
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 попыток