Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Перемещение

Присваивание передаёт владение между переменными:

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let s1 = String::from("Hello!");
    let s2 = s1;
    dbg!(s2);
    // dbg!(s1);
}
  • Присваивание s1 в s2 передаёт владение.
  • Когда s1 выходит из области видимости, ничего не происходит: она ничем не владеет.
  • Когда s2 выходит из области видимости, данные строки освобождаются.

До перемещения в s2:

StackHeaps1ptrHello!len6capacity6

После перемещения в s2:

StackHeaps1ptrHello!len6capacity6s2ptrlen6capacity6(inaccessible)

Когда вы передаёте значение в функцию, оно присваивается параметру функции. Это передаёт владение:

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn say_hello(name: String) {
    println!("{name}, привет!")
}

fn main() {
    let name = String::from("Алиса");
    say_hello(name);
    // say_hello(name);
}
This slide should take about 5 minutes.
  • Отметьте, что это противоположность поведению по умолчанию в 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 выходят из области видимости, каждая из них освобождает свою память.

До копирующего присваивания:

StackHeaps1ptrCpplen3capacity3

После копирующего присваивания:

StackHeaps1ptrCpplen3capacity3s2ptrCpplen3capacity3

Ключевые моменты:

  • C++ сделал немного другой выбор, чем Rust. Поскольку = копирует данные, данные строки приходится клонировать. Иначе при выходе из области видимости любой из строк мы получили бы двойное освобождение.

  • В C++ также есть std::move, которым обозначают, что значение может быть перемещено. Если бы в примере было s2 = std::move(s1), выделения памяти в куче не произошло бы. После перемещения s1 оказалась бы в корректном, но не уточнённом состоянии. В отличие от Rust, программисту разрешено продолжать использовать s1.

  • В отличие от Rust, = в C++ может выполнять произвольный код, определённый типом, который копируется или перемещается.