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