There is a great example of Rust's move semantics documented here: Rust Move Semantics on the Rust By Example website.
I have a basic understanding of both cases demonstrated. The first being how a primitive can have a new alias and the original can still be used because the end result is a copy seeing as i32
utilizes the Copy
trait. This makes good sense to me.
Additionally, for many good reasons the second example makes sense in terms of having multiple aliases that refer to an i32
on the heap. Rust enforces ownership rules and therefore the original alias cannot be used now that a new binding has been created. This helps prevent data-races, double frees, etc.
But it would seem there is a third case which is not talked about. How does Rust implement moves of stack allocated structs that do not implement the Copy
trait? This is illustrated with the following code:
#[derive(Debug)]
struct Employee{
age: i32,
}
fn do_something(m: Employee){
println!("{:?}", m);
}
fn main() {
let x = Employee {
age: 25,
};
do_something(x);
//compiler error below because x has moved
do_something(x);
}
This I know: In the case above, Rust will allocate the Employee
on the stack. The above struct does not implement the Copy
trait and therefore will not be copied when assigned to a new alias. This is very confusing to me because if the Employee
struct is allocated on the stack and also does not implement the Copy
trait where/how does it move? Does it physically get moved to do_something()
's stack frame?
Any help is appreciated in explaining this conundrum.
Does it physically get moved to
do_something()
's stack frame?
Yes. Non-Copy
types are physically moved exactly like Copy
types are: with a memcpy
. You already understood that primitive Copy
-types are copied into the new location (new stack frame for example) byte-by-byte.
Now consider this implementation of Box
:
struct Box<T> {
ptr: *const T,
}
When you have
let b = Box::new(27i32);
do_something(b); // `b` is moved into `do_something`
then an i32
is allocated on the heap and the Box
saves the raw pointer to that heap allocated memory. Note that the Box
directly (the raw pointer inside) is directly on the stack, not on the heap! Just the i32
is on the heap.
When the Box
is moved, it is memcpy
ed, as I just said. This means that the stack contents are copied (!!)... thus just the pointer is copied byte-by-byte. There isn't a second version of the i32
!
There is no difference between Copy
and non-Copy
types when it comes to moving physically. The only difference is that the compiler enforces different rules upon those types.
来源:https://stackoverflow.com/questions/36230710/how-does-rust-move-stack-variables-that-are-not-copyable