简体   繁体   English

rust 移动语义实际上是如何工作的

[英]how does rust move semantics actually work

I have a little confuse about the move semantics in rust after I write some code and read some articles, I thought after the value moved, it should be freed, the memory should be invalided.在我写了一些代码并阅读了一些文章之后,我对 rust 中的移动语义有点困惑,我认为值移动后,它应该被释放,memory 应该无效。 So I try to write some code to testify.所以我尝试写一些代码来作证。

the first example第一个例子

#[derive(Debug)]
struct Hello {
    field: u64,
    field_ptr: *const u64,
}

impl Hello {
    fn new() -> Self {
        let h = Hello {
            field: 100,
            field_ptr: std::ptr::null(),
        };
        h
    }

    fn init(&mut self) {
        self.field_ptr = &self.field as *const u64;
    }
}
fn main(){
    let mut h = Hello::new();
    h.init();
    println!("=================");
    println!("addr of h: ({:?}) \naddr of field ({:?})\nfield_ptr: ({:?}) \nptr value {:?}", &h as *const Hello, &h.field as *const u64, h.field_ptr, unsafe {*h.field_ptr});

    let c = &h.field as *const u64;
    let e = &h as *const Hello;
    let a = h;
    let d = &a.field as *const u64;

    println!("=================");
    println!("addr of a: ({:?}) \naddr of field ({:?})\nfield_ptr: ({:?}) \nptr value {:?}", &a as *const Hello, &a.field as *const u64, a.field_ptr, unsafe {*a.field_ptr});
    println!("=================");
    println!("addr of c {:?}\nvalue {:?}", c, unsafe {*c});
    println!("addr of d {:?}\nvalue {:?}", d, unsafe {*d});
    println!("addr of e {:?}\nvalue {:?}", e, unsafe {&*e});
}

the result of code above is上面代码的结果是

=================
addr of h: (0x7ffee9700628) 
addr of field (0x7ffee9700628)
field_ptr: (0x7ffee9700628) 
ptr value 100
=================
addr of a: (0x7ffee9700720) 
addr of field (0x7ffee9700720)
field_ptr: (0x7ffee9700628) 
ptr value 100
=================
addr of c 0x7ffee9700628
value 100
addr of d 0x7ffee9700720
value 100
addr of e 0x7ffee9700628
value Hello { field: 100, field_ptr: 0x7ffee9700628 }

so, I create a self reference struct Hello and make field_ptr point to the u64 field, and use a raw point to save the address of the struct and the address of field, and I move h to a to invalide the h variable, but I can still get the value of original variable which IMO should not exists through raw point?所以,我创建了一个自引用结构Hello并使 field_ptr 指向 u64 字段,并使用原始点来保存结构的地址和字段的地址,然后将 h 移动到 a 以使 h 变量无效,但是我仍然可以通过原始点获取IMO不应该存在的原始变量的值吗?

the second example第二个例子

struct Boxed {
    field: u64,
}
fn main(){

   let mut f = std::ptr::null();
    {
        let boxed = Box::new(Boxed{field: 123});
        f = &boxed.field as *const u64;
    }
    println!("addr of f {:?}\nvalue {:?}", f, unsafe {&*f});
}

the result结果

addr of f 0x7fc1f8c05d30
value 123

I create a boxed value and drop it after use a raw point save it's address, and I can still read the value of it's field through the raw point.我创建了一个盒装值并在使用原始点保存它的地址后将其删除,我仍然可以通过原始点读取它的字段值。

So my confuse is所以我的困惑是

  1. does move in rust actually a memcpy? rust 中的移动实际上是一个 memcpy 吗? and the original variable is just "hide" by the compiler?原始变量只是被编译器“隐藏”了吗?
  2. when does rust actually free the memory of variable on the heap? rust 什么时候真正释放堆上的变量 memory? (the second example) (第二个例子)

thanks谢谢

what I have read How does Rust provide move semantics?我读过的内容Rust 如何提供移动语义?

So the first block of your output should be clear, right?那么你的output的第一块应该清楚了吧? The address of the struct is just the first bit of memory for where that struct sits in memory, which is the same as the address of its first field.该结构的地址只是 memory 的第一位,该结构位于 memory 中,这与其第一个字段的地址相同。

Now for your second block.现在是你的第二个街区。 You're grabbing some raw pointers into the struct, and then you're moving the struct, via let a = h .您正在获取结构中的一些原始指针,然后通过let a = h移动结构。

What that does is: On the stack we now have a new variable a , a memory copy of what the old stack layout for variable h was.它的作用是:在堆栈上我们现在有一个新变量a ,它是变量h的旧堆栈布局的 memory 副本。 That's why both a and a.field have a new address.这就是aa.field都有新地址的原因。 The raw pointer, of course, still points to the old h.field address, and that's why you can still access that data.当然,原始指针仍然指向旧的h.field地址,这就是您仍然可以访问该数据的原因。

Note though that you can only do that via the unsafe block, because what you do is unsafe.请注意,您只能通过unsafe块执行此操作,因为您所做的是不安全的。 There's no guarantee that whatever your field pointer points to will remain valid.不能保证您的字段指针指向的任何内容都将保持有效。

If you remove all use of unsafe constructs, there will be no way to access a.field via h.field .如果您删除所有unsafe结构的使用,将无法通过h.field a.field

Same idea applies to the second example.同样的想法适用于第二个例子。 You couldn't get to the dropped stuff if you weren't using raw pointers and unsafe blocks, and that's because this code is very suspicious.如果您不使用原始指针和不安全的块,您将无法获得丢弃的东西,那是因为这段代码非常可疑。 In your simple example, it still works because Rust doesn't just go ahead and scramble the memory of values that have been dropped.在您的简单示例中,它仍然有效,因为 Rust 不只是前面的 go 和加扰已丢弃的值的 memory。 Unless something else in your program repurposes that memory, it will stay how you left it.除非您程序中的其他内容重新调整了 memory 的用途,否则它将保持原样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM