Ownership and References
Ownership
Объявленная переменная, обеспеченная памятью кучи (heap) - общей памятью (не стека!) всегда имеет владельца. При передаче такой переменной в другую переменную, либо в функцию, происходит перемещение указателя на переменную = смена владельца. После перемещения, нельзя обращаться к исходной переменной.
let s1 = String::from("hello"); // строка в куче создана из литералов в стеке
let s2 = s1; // перемещение
println!("{}, world!", s1); // ошибка! Вызов перемещённой переменной
Решения:
- Можно сделать явный клон переменной со значением;
let s1 = String::from("hello");
let s2 = s1.clone(); // полный клон. Медленно и затратно,
println!("s1 = {}, s2 = {}", s1, s2); // но нет передачи владения
- Передавать ссылку на указатель. Ссылка на указатель - ‘&’, раскрыть ссылку на указатель - ‘*’.
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // передача ссылки на указатель
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize { // приём ссылки на указатель
s.len()
}
References
Для внесения изменений по ссылке на указатель, нужно указать это явно через ‘mut’.
fn main() {
let mut s = String::with_capacity(32); // объявить размер блока данных заранее, чтобы потом не довыделять при закидывании данных в строку = быстрее
change(&mut s); // передача изменяемой ссылки
}
fn change(some_string: &mut String) { // приём изменяемой ссылки на указатель
some_string.push_str("hello, world");
}
Tip
Правила:
- В области жизни может быть лишь одна изменяемая ссылка на указатель (нельзя одновременно нескольким потокам писать в одну область памяти);
- Если есть изменяемая ссылка на указатель переменной, не может быть неизменяемых ссылок на указатель этой же переменной (иначе можно перезаписать данные в процессе их же чтения);
- Если ссылка на указатель переменной неизменяемая, можно делать сколько угодно неизменяемых ссылок на указатель (можно вместе читать одни и те же данные);
- Конец жизни ссылки определяется её последним использованием. Можно объявлять новую ссылку на указатель, если последняя изменяемая ссылка по ходу программы более не вызывается.
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 вышла из области жизни, поэтому можно объявить новую ссылку на указатель.
let r2 = &mut s;