Ownership
Prerequisites: Functions
Ownership is how Rust manages memory without a garbage collector. It's the feature that makes Rust different from most languages, so take this lesson slowly.
Stack and heap, briefly
Values with a fixed, known size — integers, floats, bool, char —
are stored directly on the stack, which is fast and cleaned up
automatically. Data whose size can change, like the text in a
String, lives on the heap: the String itself holds a pointer
to heap memory, plus a length and capacity. Heap memory has to be
freed exactly once, at the right time. Ownership is Rust's system for
getting that right.
The three rules
- Each value in Rust has an owner — the variable that holds it.
- There can only be one owner at a time.
- When the owner goes out of scope, the value is dropped (its memory is freed).
Syntax
let a = String::from("hello"); // `a` owns the String
let b = a; // ownership MOVES to `b`; `a` is no longer usable
let c = b.clone(); // an explicit deep copy; `b` and `c` each own one
let x = 5;
let y = x; // integers are Copy: `x` is still usable
String::from("hello") creates an owned, growable String from a
text literal (the Strings lesson covers String in depth).
- Move — assigning a
Stringto another variable, or passing it to a function, moves ownership. Only the pointer, length, and capacity are copied; the heap data isn't. The old variable becomes invalid, so the text can't be freed twice. - Clone —
.clone()copies the heap data too, giving you two independent values. It's explicit because it can be expensive. - Copy — simple stack-only types (all integers and floats,
bool,char, and tuples or arrays made only of these) are copied instead of moved. The original stays usable. - Drop — a scope ends at a closing
}. When the owner goes out of scope, Rust automatically frees the value. You never callfree.
Example
fn main() {
{
let temp = String::from("scoped");
println!("{temp}");
} // `temp` goes out of scope here and its String is dropped
let a = String::from("hello");
let b = a; // move: `a` can't be used after this
println!("b = {b}");
let c = b.clone(); // deep copy
println!("b = {b}, c = {c}");
let x = 5;
let y = x; // copy: both stay usable
println!("x = {x}, y = {y}");
let name = String::from("Ada");
takes_ownership(name); // `name` moves into the function
let n = 10;
makes_copy(n); // `n` is copied
println!("n is still {n}");
let greeting = gives_ownership();
println!("got back: {greeting}");
}
fn takes_ownership(text: String) {
println!("took: {text}");
} // `text` is dropped here
fn makes_copy(number: i32) {
println!("copied: {number}");
}
fn gives_ownership() -> String {
String::from("hi") // ownership moves out to the caller
}
Expected output
scoped
b = hello
b = hello, c = hello
x = 5, y = 5
took: Ada
copied: 10
n is still 10
got back: hi
Common mistakes
- Using a value after it moved:
let a = String::from("hello");
let b = a;
println!("{a} {b}");
error[E0382]: borrow of moved value: `a`
--> src/main.rs:4:16
|
2 | let a = String::from("hello");
| - move occurs because `a` has type `String`, which does not implement the `Copy` trait
3 | let b = a;
| - value moved here
4 | println!("{a} {b}");
| ^ value borrowed here after move
Read it from the top: where the value moved, why it moved (no
Copy), and where you tried to use it. The compiler also suggests
a.clone().
- Using a variable after passing it to a function. Passing a
Stringto a function moves it, exactly like assignment. Either return it from the function, clone it, or — usually best — pass a reference (next lesson). - Sprinkling
.clone()everywhere to silence errors. It works, but copies data needlessly; references are usually the right fix.
Practice
This program doesn't compile, because word is used after it moves
into print_length:
fn print_length(text: String) {
println!("Length: {}", text.len());
}
fn main() {
let word = String::from("hello");
print_length(word);
println!("Still usable: {word}");
}
Fix it without using .clone(), by having print_length give
ownership back to the caller. The fixed program should print:
Length: 5
Still usable: hello
Show solution
fn print_length(text: String) -> String {
println!("Length: {}", text.len());
text // return ownership to the caller
}
fn main() {
let word = String::from("hello");
let word = print_length(word);
println!("Still usable: {word}");
}