Перейти к основному содержанию

4. Заимствование (Shared Borrowing)

Заимствование позволяет нам передавать значение переменной другой переменной или функции не клонируя эти данные в памяти компьютера или не теряя владение (для перемещаемых типов данных). Для этого в Rust используются ссылки на переменные. Т.е. мы можем передать в функцию не саму переменную, а ссылку на эту переменную, чтобы функция прочитала данные из ячеек памяти закрепленных за переменной. Таким образом мы получаем доступ к данным, но не забираем их во владение. Это называется совместное использование данных или в английской терминологии Shared Borrowing.

Аналогия из жизни. Вы библиотекарь, а студенты приходят в читальный зал её прочитать. Вы остаётесь владельцем книги, а студенты уносят с собой только знания.

Чтобы программа обратилась к значению переменной по ссылке на эту переменную, следует указать знак & (амперсанд) перед переменной.

Исправляем ошибку в примере из прошлой лекции

fn main() {
	let s: String = String::from("Пример");
	let t: &String = &s;
	println!("t = {}", t);
	println!("s = {}", s);
}
Результат: t = Пример s = Пример

Вуаля! Всё работает. 

Переменной t мы не отдаем значение переменной s, а только ссылку на нее. И как только нам понадобилась t, Rust идет по ссылке, находит переменную s и уже по ссылке, прописанной в s идет в оперативную память. А переменная s остается доступной.

Механизм заимствования значений

Чтобы лучше представлять что происходит, приведу условную схему заимствования.

Заимствующая переменная (или функция) не напрямую ссылается на значение в памяти компьютера, а на переменную, значение которой мы хотим заимствовать.

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

Напомню зачем так заморачиваться со ссылками. Когда закрываются фигурные скобки, заканчивается и область действия ссылки. Соответственно, нам нужно удалить s и освободить память, которую она использовала. А переменную t мы просто удаляем и не освобождаем память потому, что она только ссылалась на значение у переменной s, не владея ничем. Так мы избежали ошибки двойного освобождения памяти.


А как там Егор и Нина поживают? Егор у нас владелец книги и не собирается отдавать её Нине, а скопировать нет времени, скоро начнется урок. Нина сядет рядом с ним за парту и прочитает то, что ей нужно из книги никак её не перемещая и не клонируя.

fn main() {
	let real_egor: String = "книга".to_string();
	let i_nina: &String = &real_egor; // Егор заимствует книгу Нине
	println!("У Нины появились знания из {}", i_nina);
	println!("У Егора осталась {}", real_egor);
}
Результат: У Нины появились знания из книга У Егора осталась книга

Множественное заимствование

Значение по ссылке можно заимствовать сколько угодно раз. Для каждого отдельного заимствования справедливы все правила, которые мы рассмотрели ранее.

fn main() {
	let s: String = String::from("Курс программирования");
	let s1: &String = &s;
	let s2: &String = &s;
	println!("s2: {}", s2);
	println!("s:  {}", s);
	println!("s1: {}", s1);
}
Результат: s2: Курс программирования s: Курс программирования s1: Курс программирования

Ещё пример

У нас в примерах везде используется String, но ссылаться можно на любой другой тип данных.
fn main() {
		let n: i8 = 8;
	let m: &i8 = &n;
	println!("m = {}", m);
	println!("n = {}", n);
}
Результат: t = Пример s = Пример

Подитожим. Заимствование производится через указание & перед переменной. При этом тип получаемой переменной является ссылкой. Заимствовать можно сколько угодно раз. Помним, что исходная переменная не может быть перемещена или удалена.

Для просмотра заданий и решений, а также публикации своих решений необходимо зарегистрироваться на сайте.

Всё бесплатно, мы просто хотим с вами познакомиться и понять насколько актуально то, что мы делаем.

© Клют И. А., 2022. Копирование контента возможно только с письменного разрешения автора.