Как можно выяснить, действительно ли объект перемещается в памяти при операции «перемещения»?

Я хочу создать простой пример для проверки перемещения объекта в памяти и выяснить, меняется ли адрес объекта, когда он передается от одного владельца к другому. Я предполагаю, что адрес не должен меняться, так как речь идет о перемещении, которое существует только в исходном коде для ссылочных типов. Эти типы выделяют память в куче, и переменная ссылается на объект. Я об этом иду разговор, когда упоминаю тип `Box`. Я ожидаю, что после перемещения данные будут по тому же адресу, поскольку если Rust будет копировать данные в куче, это будет неэффективно и не оптимально. 

Однако я не могу написать пример, чтобы он скомпилировался, без вызова метода `clone()`. <br/> <pre><code>#![allow(unused)]
fn main() {

  let b = Box::new(4);
  
  check_b(&amp;b);
  let c = b;      // Здесь происходит move
  
  check_c(&amp;c);
}

fn check_c(val: &amp;Box&lt;i32&gt;){
    println!("address c: {}\n", Box::into_raw(val.clone()) as usize);
}

fn check_b(val: &amp;Box&lt;i32&gt;){
   println!("address b: {}\n", Box::into_raw(val.clone()) as usize);
}</code></pre> <br/> В результате вызова `clone()` я не могу это проверить, поскольку данные копируются: <br/> <br/> address b: 104808743070512 <br/> <br/> address c: 104808743070544 <br/> <br/> Как мне переписать пример, чтобы проверить изменение адреса объекта? <br/> Спасибо.
  • 8 апреля 2025 г. 15:15
Ответы на вопрос 3
В вашем примере проблема в том, что при вызове метода `clone()` вы создаете новую копию объекта, и результатом является новый адрес в памяти. Чтобы проверить, изменяется ли адрес объекта при перемещении, вам нужно обойтись без клонирования.

Вот как можно переписать ваш код так, чтобы он корректно показывал адреса объекта до и после перемещения:

```rust
#![allow(unused)]
fn main() {
    let b = Box::new(4);
    
    check_b(&b);
    let c = b; // Здесь происходит перемещение
    
    check_c(&c);
}

fn check_c(val: &Box<i32>) {
    println!("address c: {}\n", Box::into_raw(val) as usize);
}

fn check_b(val: &Box<i32>) {
    println!("address b: {}\n", Box::into_raw(val) as usize);
}
```

Тем не менее, чтобы перемещение прошло успешно, вам нужно изменить функцию `check_c` так, чтобы она принимала `Box<i32>` вместо ссылки, потому что при перемещении вдали от `b` у вас не будет доступа к оригиналу.

Таким образом, мы можем выполнить следующий код:

```rust
#![allow(unused)]
fn main() {
    let b = Box::new(4);
    
    let b_address = Box::into_raw(b); // Получаем адрес до перемещения
    println!("address b: {}\n", b_address as usize);
    
    let c = unsafe { Box::from_raw(b_address) }; // Здесь происходит перемещение
    
    let c_address = Box::into_raw(c); // Получаем адрес после перемещения
    println!("address c: {}\n", c_address as usize);
}
```

В этом коде:

1. Мы используем `Box::into_raw` для получения адреса объекта в куче.
2. Присваиваем значение `b` переменной `c`. Обратите внимание, что для этого мы используем `unsafe`, чтобы создать `Box` из адреса.
3. После этого, когда мы снова получаем адрес с помощью `Box::into_raw`, вы сможете увидеть, что адрес не изменился.

Важно учитывать, что с использованием `unsafe` вы берете на себя ответственность за безопасное управление памятью, поэтому необходимо внимательно относиться к тому, как вы используете выделенную память.
Я изменил пример и получилось, что походу адрес не меняется. То есть при перемещении владения объект лежит там же в памяти: 
#![allow(unused)]
fn main() {

  let b = Box::new(4);
  
  check_b(&b);
  let c = b;          // Здесь происходит move
  
  check_c(&c);
}

fn check_c(val: &Box<i32>){
    println!("address c: {}\n", &raw const val as usize);
}

fn check_b(val: &Box<i32>){
   println!("address b: {}\n", &raw const val as usize);
}

address b: 140733161839864

address c: 140733161839864
Как проверить перемещается ли в памяти объект при «перемещении»?

В общем случае: посмотреть реализован ли Copy, поскольку у него семантика перемещения заменяется на семантику копирования. За исключением Copy все копирования и аллокации в расте явные, вроде методов clone() или to_vec().

В случае с Box вы уже сами пришли к ответу, достаточно было просмотреть на адрес в указателе.

Ну и код проверки какой-то странный, достаточно этого
fn main() {
    let b = Box::new(4);
    check(&b);
    let c = b;
    check(&c);
}

fn check(val: &i32) {
    println!("address: {:p}", val);
}
Похожие вопросы