Before you begin
Why this matters
Ownership is how Rust frees memory without a garbage collector (a part of the runtime that frees memory the program can no longer reach) and without you calling free. Once you can name the owner of every value, the compiler’s ownership errors become instructions you can follow, and you can choose deliberately between moving, borrowing, and cloning.
What this helps you reason about
- Name the owner of a value at any line, and predict when it is cleaned up.
- Tell a move, a copy, and a clone apart.
- Read errors E0382 (“borrow of moved value”) and E0507 (“cannot move out of index”) as facts about ownership, and fix them with the right tool.
In Python, b = a makes two names share one object, and a garbage collector frees the object later. In C, b = a copies the value. In Rust, for a String, let b = a does neither: the String moves to b, and a can no longer be used. This lesson follows ownership through assignments and calls until that rule is predictable.
Already know this? The short version
- Every value has exactly one owner. The value is dropped (its Drop code runs and any heap memory it owns is freed) when the owner’s scope ends, and values in one scope are dropped in reverse order of declaration.
- Assigning or passing a value that is not Copy, such as a String, moves it. The pointer, length, and capacity are copied, the heap text is not, and the compiler rejects any later use of the old binding with E0382.
- Copy types, such as integers, floats, bool, char, and tuples or arrays of them, are duplicated instead. A type that owns heap memory or implements Drop cannot be Copy.
- clone makes an independent value at the cost of allocating and copying. Returning a value moves ownership back to the caller, and a reference (&T) lends a value without moving it.
- The section “What goes wrong with ownership in real programs?” covers clone in a loop, values moved into a closure or thread, moving out of a field behind &mut with std::mem::take, and let _ dropping a value at once.
Words in this lesson (8)
- owner
- The one binding, struct field, or collection that holds a value and is responsible for freeing it. A value has exactly one owner at a time. When the owner goes out of scope, Rust drops the value and frees its memory.
- scope
- The part of the program where a binding can be used: from its let to the closing brace of the block around it. A block is the code between a pair of braces.
- drop
- What Rust does to a value when its owner goes out of scope: it runs the value’s cleanup code, if its type has any, and frees any memory the value owns elsewhere, such as a String’s text or a Box’s contents. A type provides cleanup code by implementing the Drop trait.
- heap
- An area of memory for data whose size or lifetime is decided while the program runs, so it can stay usable after the call that created it returns. Box, String, and Vec keep their contents there.
- move
- Assigning, passing, or returning a value whose type is not Copy, such as a String. Ownership goes to the new binding, and the compiler rejects any later use of the old one.
- borrow
- Using a value through a reference, such as &label, without taking ownership. The owner keeps the value.
- Copy
- A trait (a named set of abilities a type can have) for types whose bytes are a complete value, such as i32 or bool. Assigning a Copy value copies it, and the old binding stays usable.
- clone
- A method that makes a second value of the same type, with its own owner. For most types, such as String and Vec, the copy is independent and gets new heap memory for its contents. For Rc, clone makes a second pointer to the same shared value and adds 1 to its count.
Examples are focused snippets; imports, enclosing functions, or classes may be omitted. C examples use C11; Haskell examples use Haskell 2010. Expand the notes for language-specific depth.
01 / One owner
Who owns a value?
Every value in Rust has an owner: the binding (or struct field, or collection) that holds it. There is exactly one owner at a time. A binding’s scope runs from its let to the closing brace of its block. When the owner goes out of scope, Rust drops the value: it runs the value’s cleanup code, if its type has any, and frees any memory the value owns elsewhere, such as a String’s text. A value with no such memory, such as an i32, simply stops existing.
So cleanup happens at a place you can see in the code, without a garbage collector and without calling free.
{
let label = String::from("ready"); // label owns the String
println!("{}", label);
} // label's scope ends: the String is dropped here One owner per value; the value is dropped when its owner’s scope ends.
Coming from Python: Python frees an object some time after nothing can reach it; Rust drops a value at a closing brace you can point to.
Precisely: drop order
Values are dropped in reverse order of their declaration within a scope, so later values can safely refer to earlier ones while they are cleaned up. A type implements the Drop trait to run its own cleanup, as Badge does in the experiment. Dropping at the end of scope is what normally happens, not a promise: std::mem::forget, Box::leak, and a cycle of Rc values are allowed in safe Rust and skip the drop, so that memory is never freed. When a value really needs several owners, Rc and Arc keep a count of them and drop the value when the last one goes away; Stack and heap introduces both.
Experiment
Watch values dropped when their owner’s scope ends
Each Badge has one owner, the binding that holds it. When the owner’s scope ends, Rust calls drop on the value. Inner scopes end first, and within a scope values are dropped in reverse order of creation.
Why it matters Cleanup happens at a point you can read from the code: the end of the owner’s scope.
Predict the order of all four lines. Then move let _second out of the inner block, just above it, and predict again.
struct Badge(u32);
impl Drop for Badge {
fn drop(&mut self) {
println!("drop badge {}", self.0);
}
}
fn main() {
let _first = Badge(1);
{
let _second = Badge(2);
println!("inner block ends");
}
println!("main ends");
}
What to look for
Watch the output panel. A drop line appears exactly when a closing brace is reached. The stepper also shows each drop call as its own frame, named main::{impl#0}::drop, above main.
Expected output
inner block ends
drop badge 2
main ends
drop badge 102 / Moves
What happens when you assign a String?
A String has two parts. Its text is on the heap, an area of memory for data whose size is decided while the program runs. Its small part holds a pointer (the address of the text), a length (how many bytes the text uses), and a capacity (how many bytes fit in the space reserved for it).
let second = first; copies the small part into second and makes second the owner. The text itself is not copied. If first and second could both be used, both would free the same text when they go out of scope. Freeing the same memory twice is called a double free, and it can corrupt memory. So first stops being an owner, and the compiler rejects any later use of first:
error[E0382]: borrow of moved value: `first`
The error marks three places: “move occurs because `first` has type `String`, which does not implement the `Copy` trait” at the let, “value moved here” under let second = first, and “value borrowed here after move” at the later use. It says borrow because println! takes its arguments through references, and even a borrow is a use. To fix it, use second from that line on, or call first.clone() if you need two Strings, as a later section shows.
Taking a String out of a Vec is a move too. A Vec, a growable list written vec![...], owns its elements, so let one = names[0]; would move a String out and leave a hole in the Vec. The compiler refuses:
error[E0507]: cannot move out of index of `Vec<String>`
with “help: consider borrowing here: `&names[0]`”. A borrow, as in the Variables lesson, uses a value without taking ownership: &names[0] reads the element while the Vec stays its owner. names[0].clone() also works, at the cost of copying the text.
let first = String::from("ready");
// the String moves to second
let second = first;
// error[E0382], because first was moved:
// println!("{}", first);
println!("{}", second);
let names = vec![String::from("ada"), String::from("lin")];
// error[E0507], because it would leave a hole:
// let one = names[0];
// borrow the element instead
let one = &names[0];
println!("{} {}", one, names.len()); Assigning a String moves it; the old binding cannot be used.
Coming from Python: In Python, two names can share one object; Rust refuses to give a value two owners, so the first name gives the String up.
Precisely: what a move does at run time
At run time a move is a copy of the small part, which an optimizer often removes. The debugger may still show the old binding’s bytes after a move. What changes is what the compiler allows you to write. In C, where you call free yourself, a double free is undefined behavior: it typically corrupts the records malloc keeps about which memory is in use, so the program may crash much later. names.remove(0) is another way to take an element out of a Vec: it moves the element out and shifts the rest down. The error messages in this lesson are quoted as rustc 1.63, the stepper’s compiler, prints them.
Experiment
Move a String and look at the old owner
let second = first; moves the String: the small value holding the pointer, length, and capacity is copied into second, and first stops being an owner. The text on the heap is not copied. Only second will free it.
Why it matters A move is a rule the compiler checks, not something that happens to memory at run time. It guarantees the text is freed exactly once, by its one owner.
Predict what prints. Then add println!("{}", first); as the last line and read the compiler error.
fn main() {
let first = String::from("ready");
let second = first;
let length = second.len();
println!("{} {}", second, length);
}
What to look for
After let second = first;, look at both bindings. The debugger still shows first with the same bytes and the same data= address as second. Nothing was erased; the compiler simply will not let you use first again. With the extra line, the compiler reports error[E0382]: borrow of moved value: `first`, with “value moved here” under let second = first.
Expected output
ready 503 / Calls
What happens when you pass it to a function?
Passing a value to a function works like assignment: the parameter becomes the owner. If the function does not return the value, the value is dropped when the function ends. The caller can no longer use it: a later println!("{}", message) gives the same error[E0382]: borrow of moved value: `message`, with “value moved here” under shout(message).
A for loop takes ownership the same way. for name in names moves the whole Vec into the loop, so a later names.len() gives error[E0382]: borrow of moved value: `names`, with the label “`names` moved due to this implicit call to `.into_iter()`”. into_iter is the method a for loop calls to get the elements. The compiler’s help suggests for name in &names, which borrows each element and leaves the Vec with its owner.
If the caller still needs a value, the function should borrow it: take &String and pass &message. The next lesson, Rust borrowing and references, covers borrowing; its link is at the end of this page.
fn shout(text: String) {
println!("{}!", text);
} // text is dropped here
let message = String::from("ready");
// message moves into shout
shout(message);
// error[E0382], because message was moved:
// println!("{}", message);
let names = vec![String::from("ada"), String::from("lin")];
// borrows each name; for name in names would move the Vec
for name in &names {
println!("{}", name);
}
// fine: names still owns the Vec
println!("{}", names.len()); A parameter taken by value becomes the new owner.
Coming from JavaScript: Passing an object to a JavaScript function never takes it away from the caller; passing a String by value in Rust does.
Experiment
Move a value into a function
Passing badge to show moves ownership into the parameter. show is now the owner, so the Badge is dropped when show returns. A reference (&Badge) lends the value instead; main stays the owner.
Why it matters Taking a parameter by value means “give it to me”. If the caller still needs the value afterwards, the function should borrow it.
Predict the order of the three lines. Then try to print badge.0 after the call to show and read the error. Finally, change show to take &Badge and pass &badge.
struct Badge(u32);
impl Drop for Badge {
fn drop(&mut self) {
println!("drop badge {}", self.0);
}
}
fn show(badge: Badge) {
println!("showing badge {}", badge.0);
}
fn main() {
let badge = Badge(7);
show(badge);
println!("after the call");
}
What to look for
Watch when “drop badge 7” appears: at the end of show, not at the end of main. With &Badge, “drop badge 7” appears last, after “after the call”, at the end of main. Printing badge.0 after the call is rejected with error[E0382]: borrow of moved value: `badge`, with “value moved here” under show(badge).
Expected output
showing badge 7
drop badge 7
after the call04 / Copy
Why do integers not move?
The Variables lesson showed that an i32 is Copy: assigning it copies the number, and the old binding stays usable. Passing a Copy value to a function copies it the same way, so the caller keeps its own.
Copy is allowed only for types whose bytes are the whole value: integers, floating-point numbers, bool, char, and tuples or arrays made only of these. A String cannot be Copy. Copying its small part would give two owners of one text, and the double free from the section on assignment. That is what the E0382 label “which does not implement the `Copy` trait” refers to.
fn double(n: i32) -> i32 {
n * 2
}
let count = 3;
let twice = double(count); // count is copied in
println!("{} {}", count, twice); // 3 6 Copy types are copied when assigned or passed; everything else moves.
Coming from C: C lets you copy a struct that holds a pointer to malloc memory, which leaves two owners; Rust makes such a type move instead.
Precisely: deriving Copy
Your own struct can be Copy if every field is Copy: write #[derive(Clone, Copy)]. A struct containing a String, or any type with its own Drop, cannot be Copy. Copy says nothing about cost: [i32; 100000] is Copy, and copying it copies 400,000 bytes.
05 / Clone
How do you get a second, independent value?
When you really need two independent Strings, call clone. clone copies the heap text into new memory, so each String has its own text and its own owner. Changing one does not change the other.
clone does real work: it reserves new heap memory and copies the text into it. Use it when you need two values, not to make a compiler error go away.
let first = String::from("ready");
// new text, new owner
let mut second = first.clone();
second.push('!');
println!("{} {}", first, second); // ready ready! clone makes a second, independent value, at the cost of copying it.
06 / Giving it back
How does ownership come back to the caller?
A function can give ownership back by returning the value. The caller’s binding becomes the new owner. In add_mark below, mut text lets the function change the String it now owns.
Passing a value in and returning it again works, but it is clumsy: every caller has to store the returned value in a new binding. A function that only needs to look at a value borrows it instead.
fn add_mark(mut text: String) -> String {
text.push('!');
text // ownership moves back to the caller
}
let message = add_mark(String::from("ready"));
println!("{}", message); // ready! Returning a value moves ownership back to the caller.
07 / In production
What goes wrong with ownership in real programs?
Code that compiles can still use ownership badly. Four mistakes are common. If the compiler says E0382 or E0507, look at the first three.
Cloning only to make the code compile. When E0382 or E0507 appears inside a loop, adding .clone() makes the code compile, but then every pass of the loop allocates and copies. First ask whether the loop only reads the value, so it can borrow it, or whether this is the last use, so it can move it.
Moving into a closure. thread::spawn(move || …) moves every value the closure uses into the new thread. A later use of names in the function that started the thread gives error[E0382]: borrow of moved value: `names`, with “value moved into closure here” under move ||. If that function needs the value again, return it from the closure: handle.join().unwrap() gives back what the closure returned.
Moving out of a field behind &mut. In a method that takes &mut self, let messages = self.messages; gives error[E0507]: cannot move out of `self.messages` which is behind a mutable reference. The struct must still own a complete value afterwards. std::mem::take moves the value out and leaves an empty one, as below. It works only for a type that has an empty value, which is what the Default trait provides; Vec, String, and Option have one. Option::take leaves None. std::mem::replace leaves a value you choose, so it works for any type.
Dropping at once with let _. let _ = value; binds no name, so the value is dropped at the end of that statement. let _guard = value; keeps it until the end of the scope. The guard that Mutex::lock returns unlocks when it is dropped, so let _ = unlocks at once.
struct Inbox {
messages: Vec<String>,
}
impl Inbox {
fn read_all(&mut self) -> Vec<String> {
// error[E0507]: let messages = self.messages;
std::mem::take(&mut self.messages)
}
} Borrow instead of cloning in a loop, take values out of fields with std::mem::take, and name a value that must live to the end of its scope.
Experiment
Drop a value at once with let _
let _ = matches the value with the pattern _, which binds nothing. No binding owns the value after the statement, so it is dropped at the end of that statement. _first is an ordinary name; the underscore at its start only tells the compiler not to warn that it is unused. That binding owns the value until the end of main.
Why it matters When a value does its work in drop, such as a lock guard that unlocks, give it a name like _guard so it lives until the end of the scope.
Predict the order of the three lines. Then change let _ = Badge(1); to let _first = Badge(1); and predict again.
struct Badge(u32);
impl Drop for Badge {
fn drop(&mut self) {
println!("drop badge {}", self.0);
}
}
fn main() {
let _ = Badge(1);
let _second = Badge(2);
println!("main ends");
}
What to look for
At the first line of main, the stepper enters drop for Badge 1 before it reaches the next line, and “drop badge 1” is printed first. Main’s frame never lists Badge 1, because no name owns it; it lists only _second. After the edit, the frame lists _first and _second, and both drops run after “main ends”: badge 2 first, then badge 1.
Expected output
drop badge 1
main ends
drop badge 208 / Try it
Can you name every owner?
Name the owner of each String at every line, then predict the output.
fn take_and_return(text: String) -> String {
text
}
fn main() {
let first = String::from("one");
let second = first.clone();
let third = take_and_return(first);
let count = 2;
let copy = count;
println!("{} {} {} {}", second, third, count, copy);
}
What prints? Which binding owns each String at the end?
Reveal the reasoning
one one 2 2. second owns a clone of first’s text. first moves into take_and_return, which returns it, so third owns the original String and first can no longer be used. count is an i32, which is Copy, so both count and copy are usable.
Practice tickets
- RUST-101 · Lend a book without copying it → Free
- RUST-102 · Merge playlists without copying tracks → Sign in · Pro
- RUST-103 · Morning finds reach the office twice → Sign in · Pro
Keep exploring
Rust borrowing and references →