Reputation: 3042
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. 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?
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
thanks
what I have read How does Rust provide move semantics?
Upvotes: 3
Views: 743
Reputation: 9617
So the first block of your output should be clear, right? 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.
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
.
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. That's why both a
and a.field
have a new address. The raw pointer, of course, still points to the old h.field
address, and that's why you can still access that data.
Note though that you can only do that via the unsafe
block, because what you do is 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
.
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. Unless something else in your program repurposes that memory, it will stay how you left it.
Upvotes: 4