Rust with diagrams: Ownership 2 - Data interacting with Move

Rust with diagrams: Ownership 2 - Data interacting with Move

Rust documentation with diagrams to fast learn and remember. Let's learn together in Chapter 4.1.

Before delve deeper into how data interacts in memory, you should know about Rust's memory apporach.

When we are writing code that assigns data into Heap, we have two ways to handle variables and data interaction in memory: with Move and with Clone.

Variables & Data interacting with Move

Variables and data interaction with move means that data stored at the pointer address will be invalidated each time that is assigned to another variable. Therefore, if you want to use previous variables (which have been invalidated), Rust's compiler will throw an error. Let's to see an example:

let s1 = String::from("hello"); // 1. Assign "hello" into the Heap
let s2 = s1; // 2. Assign s1 to s2 invalidating s1

println!("{}, world!", s1); // 3. Rust throws an error because it can't read invalidated vairable

Let's see what's happening under the hood:

1. Assign "hello" into the Heap

Two tables: the first table contains the representation of s1 on the
stack, consisting of its length (5), capacity (5), and a pointer to the first
value in the second table. The second table contains the representation of the
string data on the heap, byte by byte.

"s1" group (ptr, len, capacity) is stored in the Stack where:

  • "ptr" is the pointer address where data "hello" is stored in the Heap (as shown in the right table).

  • "len" is how much memory (in bytes) are currently using.

  • "capacity" indicates the total amount of memory (in bytes) that the Stack group "s1" (shown in the table above) is received from the allocator.

2. Assign s1 to s2 invalidating s1

  • Stack data (s1) is copied (pointer, length and capacity), while Heap data is reassigned, not copied.

  • Rust calls the drop function to clean up the previous Stack data (s1), invalidating it.

  • This process is similar to a "shallow copy" but Rust also invalidates the original value.

3. Rust throws an error because it can read invalidated data

In the point 2, we discussing how Rust cleans up "s1" from the Stack, which means that it no longer exists and Rust cannot access it. Rust will provide an error if you are attempt to access an invalidated variable. Let's display the error:

cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:28
  |
2 |     let s1 = String::from("hello");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |
5 |     println!("{}, world!", s1);
  |                            ^^ value borrowed here after move
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let s2 = s1.clone();
  |                ++++++++

As you can see, the error message is very clear. It points out that "s1" is "value borrowed here after move" and suggests considering using clone to fix this error.

Next article

Once we see how variables & data interacting with move, i will summarize how variables & data interacting with clone.

Read next article


I wish that this content helps you to learn Rust while i do it. If you have doubts, suggestions or you see errors, please don't be shy and comment on them. The goal of this content is learn together!

References: