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

Правила:

  1. В области жизни может быть лишь одна изменяемая ссылка на указатель (нельзя одновременно нескольким потокам писать в одну область памяти);
  2. Если есть изменяемая ссылка на указатель переменной, не может быть неизменяемых ссылок на указатель этой же переменной (иначе можно перезаписать данные в процессе их же чтения);
  3. Если ссылка на указатель переменной неизменяемая, можно делать сколько угодно неизменяемых ссылок на указатель (можно вместе читать одни и те же данные);
  4. Конец жизни ссылки определяется её последним использованием. Можно объявлять новую ссылку на указатель, если последняя изменяемая ссылка по ходу программы более не вызывается.
let mut s = String::from("hello");
{
    let r1 = &mut s;
} // r1 вышла из области жизни, поэтому можно объявить новую ссылку на указатель.
    let r2 = &mut s;