Для управления данными в куче в Rust применяется такая концепция как ownership (можно перевести как "владение", "принадлежность"). Она является одной из характерных возможностей и особенностей языка Rust, которая позволяет гарантировать безопасность памяти.
Ownership имеет следующие аспекты:
Каждое значение в Rust имеет переменную, которая называется "владельцем" (owner).
У значения в один момент времени может быть только один владелец.
Когда владелец выходит за пределы области видимости/контекста, в котором он определен, его значение удаляется из памяти. Для этого Rust автоматически вызывает специальную функцию - drop(), которая очищает память.
Рассмотрим простейшую ситуацию:
fn main(){ let s1 = "hello".to_string(); // s1 - владелец строки "hello" println!("s1: {}", s1); let s2 = s1; // меняем владельца строки на s2 println!("s1: {}", s1); // !Ошибка - s1 теперь нельзя использовать println!("s2: {}", s2); }
Вначале определяем переменную s1
, которая является владельцем строки "hello". Строка "hello" хранится в куче.
Потом меняем владельца, присваивая значение s1 переменной s2.
let s2 = s1;
Но у значения может быть только один владелец. Поэтому s1 перестает быть владельцем. Таким образом, владение строкой "hello" переходит от переменной s1 к переменной s2.
А переменная s1
теперь становится неинициализированной, и соответственно нам ее нельзя использовать. В итоге при компиляции этого кода мы получим
ошибку на строке
println!("s1: {}", s1); // !Ошибка - s1 теперь нельзя использовать
Хотя мы можем заново инициализировть переменную s1, присвоив ей какое-либо значение:
fn main(){ let mut s1 = "hello".to_string(); // s1 - владелец строки "hello" println!("s1: {}", s1); let s2 = s1; // меняем владельца строки на s2 s1 = "world".to_string(); // s1 - владелец строки "world" println!("s1: {}", s1); // s1: world println!("s2: {}", s2); // s2: hello }
Передача значения параметру в функцию аналогична присвоению значения переменной:
fn main(){ let s1 = "hello".to_string(); // s1 - владелец строки "hello" println!("s1: {}", s1); display_message(s1); println!("s1: {}", s1); // !Ошибка - переменная s1 неинициализирована } fn display_message(message: String){ println!("message: {}", message); }
Здесь значение переменной s1
передается параметру message
при вызове функции display_message()
:
display_message(s1);
Это приводит к тому, что опять же переменная s1
теряет владение над строкой "hello", которое переходит к параметру message
.
В итоге опять же при компиляции мы получим ошибку на строке:
println!("s1: {}", s1); // !Ошибка - переменная s1 неинициализирована