Variable locations in Rust during copy and move
Ownership is a well-known concept in Rust and is a major reason for the language's memory safety. Consider the following example of ownership from The Rust Programming Language:
let s1 = String::from("hello"); let s2 = s1; println!("{}, world!", s1); |
Compiling this code will produce an error because the String value that is bound to the variable s1 is moved to s2. After a move, you may no longer use the value bound to s1. (You may, as we will see, re-use its memory by binding a new value of the same type to it.)
String is a container type, which means that it contains both metadata on the stack and a pointer to data on the heap. Simple types such as i32, on the other hand, are normally stored entirely on the stack. Types such as these implement the Copy trait. This means that variable reassignment does not produce a compile-time error like it does in the example above:
let x = 42; let y = x; // The value bound to x is Copy, so no error will be raised. println!("{}", x); |
String is not Copy because it contains a pointer to data on the heap. When a variable that is bound to a String is dropped, its data is automatically freed from the heap. If a String were Copy, the compiler would have to determine whether the heap data is still pointed to by another variable to avoid a double free error.
Memory layout in copies and moves
What's happening inside a program's memory during copies and moves? Consider first a move:
In the above code snippet, addresses of variables are printed by using the pointer formatter :p. I create a String value and bind it to the variable x. Then, the value is moved to the variable y which prevents me from using x again in the println! macro.
However, I can assign a new String value to x by reusing its memory on the stack. This does not change its memory address as seen in the output below:
Memory address of x: 0x7ffded2aa3d0 Memory address of y: 0x7ffded2aa3f0 Memory address of x: 0x7ffded2aa3d0 # Memory address of x is unchanged after reassignment |
A copy is similar. In the snippet below, an integer that is originally bound to x is copied to y, which means that I can still refer to x in the println! macro.
Both the copy operation and value reassignment do not change the memory locations of x as seen in the program's output:
Summary
Move semantics on container types are one of the reasons for Rust's memory safety. Nothing mysterious is happening in memory when a value is moved from one location to another. The original stack memory still exists; its use is simply disallowed by the compiler until a new value is assigned to it.
The complete program from this post may be found here: https://gist.github.com/kmdouglass/e596d0934e15f6b3a96c1eca6f6cd999
Comments
Comments powered by Disqus