Включает ли семантика перемещения в Rust копирование данных?

Мне интересно узнать о «семантике перемещения» в Rust и о том, копируются ли данные при передаче права собственности. Вот демо-код:

#[derive(Debug)]
struct Foo {
    name: i32,
    age: i32,
}

fn main() {
    let foo = Foo { name: 111, age: 1 };

    let ptr_foo = std::ptr::addr_of!(foo);
    println!("Address of foo: {:p}", ptr_foo);

    let bar = foo;

    let ptr_bar = std::ptr::addr_of!(bar);
    println!("Address of bar: {:p}", ptr_bar);
}

Я изначально подумал, что «есть переменная foo, и после перемещения мы просто переименовали ее в bar», тем самым избежав необходимости копировать соответствующие ей данные.

Для дальнейшего исследования я использовал функцию отладки в VSCode с плагином под названием «Hex Editor» и обнаружил, что оба foo и bar (адреса памяти 0x7fffffffd7d8 и 0x7fffffffd828) содержат идентичные данные (6F 00 00 00 01 00 00 00).

Означает ли это, что Rust действительно выполняет операцию копирования даже во время move, например, в данном случае копирование структуры? Может ли это поведение меняться в зависимости от того, находится ли оно в режиме выпуска?

«копирование» и «глубокое копирование» — это не одно и то же, на самом деле они совершенно разные, и ваша путаница, похоже, начинается именно с этого. Я бы предложил удалить все ссылки на то, что вам сказал ChatGPT, и вместо этого просто описать свое понимание.

user2407038 21.06.2024 18:37
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
1
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

«Перемещение» во многих языках (не только в Rust) — это семантический термин, который относится к передаче права собственности и не обязательно определяет механизм этой передачи. В Rust перемещение значения копирует данные куда-то еще, но оставляет источник значения непригодным для использования (если только этот источник позже не будет повторно инициализирован действительным значением или если перемещаемый тип не реализует Copy).

В Rust это реализовано с помощью поразрядного копирования исходного значения в место назначения. На более высоких уровнях оптимизации эту копию можно исключить, а может и нет.

Обратите внимание, что копия распространяется только на значения, содержащиеся непосредственно в перемещаемом значении. Например, при перемещении Vec копируется указатель данных, длина и емкость Vec, но не фактические элементы Vec, которые находятся за этим указателем. Это позволяет вам перемещать любой Vec за одну и ту же стоимость — перемещение огромного Vec и пустого Vec требует копирования одинакового количества материала (3 значения размером с указатель).

В вашем коде использование адреса foo и bar, скорее всего, не позволит оптимизатору фактически исключить копию, поскольку он не может объединить их расположение в памяти. Если сделать это и вернуть один и тот же адрес для обеих переменных, это нарушит правило как если бы.

Другими словами:

Если вы пытаетесь увидеть, как будет компилироваться код, не меняйте этот код для проверки значений изнутри кода, иначе вы почти наверняка каким-то образом измените поведение оптимизатора. Вместо этого посмотрите на сгенерированный машинный код.

Другие вопросы по теме