Семантика переміщення
Присвоєння переміщує володіння між змінними:
fn main() { let s1: String = String::from("Привіт!"); let s2: String = s1; println!("s2: {s2}"); // println!("s1: {s1}"); }
- Присвоєння
s1
доs2
переміщує володіння. - Коли
s1
виходить за межі області видимості, нічого не відбувається: вона нічим не володіє. - Коли
s2
виходить за межі, дані рядка звільняються.
Перед переміщенням до s2
:
Після переміщення до s2
:
Коли ви передаєте значення функції, це значення присвоюється параметру функції. Це переміщує володіння:
fn say_hello(name: String) { println!("Привіт {name}") } fn main() { let name = String::from("Alice"); say_hello(name); // say_hello(name); }
-
Зауважте, що це протилежність поведінки за замовчуванням у C++, яка копіює за значенням, якщо ви не використовуєте
std::move
(і конструктор переміщення визначено!). -
Переміщується лише володіння. Чи генерується машинний код для маніпулювання самими даними - це питання оптимізації, і такі копії агресивно оптимізуються.
-
Прості значення (наприклад, цілі числа) можна позначити
Copy
(див. наступні слайди). -
У Rust клони є явними (за допомогою
clone
).
У прикладі say_hello
:
- З першим викликом
say_hello
main
втрачає володінняname
. Після цьогоname
більше не можна використовувати вmain
. - Пам’ять купи, виділена для
name
, буде звільнено в кінці функціїsay_hello
. main
може зберігти володіння, якщо передастьname
як посилання (&name
) і якщоsay_hello
приймає посилання як параметр.- Крім того,
main
може передати клонname
під час першого виклику (name.clone()
). - Rust ускладнює ненавмисне створення копій, на відмінну від C++, роблячи семантику переміщення за замовчуванням і змушуючи програмістів робити клони явними.
Більше інформації для вивчення
Захисні копії в сучасному C++
Сучасний C++ вирішує це інакше:
std::string s1 = "Cpp";
std::string s2 = s1; // Дублювання даних в s1.
- Дані купи з
s1
дублюються, аs2
отримує власну незалежну копію. - Коли
s1
іs2
виходять за межі видимості, кожен з них звільняє власну пам'ять.
Перед копіюванням:
Після копіювання:
Ключові моменти:
-
C++ зробив дещо інший вибір, ніж Rust. Оскільки
=
копіює дані, дані рядка потрібно клонувати. Інакше ми отримаємо подвійне звільнення, коли будь-який рядок виходить за межі видимості. -
C++ також має
std::move
, який використовується щоб вказати коли значення можна перемістити. Якби приклад бувs2 = std::move(s1)
, розподілу купи не відбулося б. Після переміщенняs1
буде в діючому, але не визначеному стані. На відміну від Rust, програмісту дозволено використовуватиs1
. -
На відміну від Rust,
=
у C++ може виконувати довільний код, який визначається типом, який копіюється або переміщується.