Raw data, clear context.

[
[
[

]
]
]

Assign a String to a second variable, then try using the first name. Rust rejects that use because ownership has moved.[1] Following the owner explains which name remains available and where the text will be cleaned up.[1]

Follow one value

Each Rust value has an owner. There is one owner at a time; when the owner goes out of scope, the value is dropped.[1] A scope is the region where a variable is valid, often bounded by braces.[1]

Tested on Linux ARM64 with rustc 1.99.1 and Cargo 1.99.1. Create a fresh project, then open its ownership folder in your editor and terminal.

Shell
cargo new --vcs none ownership
Output
$ cargo new --vcs none ownership
Creating binary (application) `ownership` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
exit=0

Replace src/main.rs with this complete programme.

src/main.rs
Rust
fn main() {
let original = String::from("notes");
let current = original;
println!("{current}");
}

String::from("notes") creates an owned String from the quoted text. String owns its contents in a heap-allocated buffer, an area of memory holding the text.[2] Here, original is its first owner.

The next statement moves that String into current. The existing text buffer stays allocated, and original becomes unavailable for reading.[1] At the closing brace of main, current goes out of scope and its String releases the buffer.[1]

Run from the ownership folder. Captures include the executed command and exit status.

Shell
cargo run --quiet
Output
$ cargo run --quiet
notes
exit=0
Two snapshots of one text buffer containing notes. Before assignment, original owns the buffer. After let current = original, current owns the same buffer and original is unavailable for reading. The buffer is released when current leaves scope.
Assignment moves this String from original to current. The two panels show the same text buffer before and after the move. At the end of main, current leaves scope and releases it.

A common error: using the old owner

Replace src/main.rs with this deliberately failing version. The extra final line attempts to read original after the move.

src/main.rs
Rust
fn main() {
let original = String::from("notes");
let current = original;
println!("{current}");
println!("{original}");
}

Check this version. Failure is expected.

Shell
cargo check --quiet
Output
$ cargo check --quiet
error[E0382]: borrow of moved value: `original`
--> src/main.rs:5:16
|
2 | let original = String::from("notes");
| -------- move occurs because `original` has type `String`, which does not implement the `Copy` trait
3 | let current = original;
| -------- value moved here
4 | println!("{current}");
5 | println!("{original}");
| ^^^^^^^^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let current = original.clone();
| ++++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error
exit=101

Our check returned E0382 and exit 101. Its arrows identify the declaration, the move and the later attempted use.

E0382 reports use of a value after it has moved.[3]

Remove the final line to restore the working programme. Read the move location before changing types or adding mut.

This behaviour depends on the type. Integers implement Copy, so assigning an integer allows both variables to remain usable. String moves in this assignment.[1]

Exercise

Restore the working version. Rename current to saved and change notes to shopping. Run it and expect shopping.

Next: references let a function borrow a value while its caller keeps ownership.

Browse Rust, one step at a time

Sources

  1. The Rust Book: What Is Ownership?
  2. Rust standard library: String
  3. Rust error E0382: use after move

Leave a Reply