Step 2 of 6
Owners and scope
Rust's answer to "who frees the heap memory, and when?" is a short set of rules. Learn them by heart: everything in this module and the next two follows from them.
The ownership 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: Rust frees its memory.
A variable's scope is the stretch of code where it can be used: from its let to the } that closes the block it was declared in. A block is any { ... }: a function body, an if branch, a loop body, or a bare pair of braces.
fn main() {
let arena = String::from("Crab Arena"); // arena is valid from here...
{
let fighter = String::from("Ferris"); // fighter is valid from here...
println!("{} enters {}", fighter, arena);
} // ...to here: fighter is dropped
println!("{} is empty again", arena);
} // ...to here: arena is dropped
Ferris enters Crab Arena
Crab Arena is empty again
At each closing brace, Rust drops the variables declared in that block, newest first. Dropping a String frees its heap text. You never write that code: the compiler adds it at exactly the place where the owner's scope ends. So memory is freed deterministically (you can point at the line where it happens), not "at some point later" as with a garbage collector.
This idea of tying a resource to a scope works for more than memory: files close themselves, network connections hang up and locks unlock when their owner goes out of scope. C++ programmers call it RAII ("resource acquisition is initialization"). In Rust it's how every value works.
Using a variable outside its scope
fn main() {
{
let ticket = String::from("T-100");
println!("{}", ticket);
}
println!("{}", ticket);
}
error[E0425]: cannot find value `ticket` in this scope
--> src/main.rs:6:20
|
6 | println!("{}", ticket);
| ^^^^^^
|
help: the binding `ticket` is available in a different scope in the same function
After the inner block ends, ticket and its text are gone. If you need a value after a block, declare it before the block, in the outer scope. Or let the block hand the value out as its result, like an if expression does:
fn main() {
let points = 72;
let rank = if points >= 50 {
String::from("silver")
} else {
String::from("bronze")
};
println!("{} points: {}", points, rank);
}
72 points: silver
The String is created inside a branch, but the branch hands it to rank, which lives in the outer scope, so it isn't dropped at the branch's }. Handing a value from one owner to another is called a move: the next step.
Common mistakes
- Declaring inside a loop or
if, using it after. Aletinside a loop body creates a fresh variable every round and drops it at the end of that round. - Looking for a way to free memory yourself. There's no
freeto call. (There is a way to drop a value early,drop(value), which you'll meet in step 5.) - Thinking scope means "until the function returns". It ends at the closing brace of the innermost block that contains the
let.
Your task
This program doesn't compile because weapon is used after its scope has ended. Move its declaration so it lives long enough, and make the program print:
Ferris picks up the claw
Ferris still holds the claw
Challenge 2
This program should build a shopping list in one String and print it after the loop, but list is created inside the loop body: it's a new, empty String every round, dropped at the end of each round, and it doesn't exist after the loop at all. Fix it so the program prints:
list: apples;bread;milk;
Challenge 3
The program reads a number of points and should print a rank: gold for 100 or more, silver for 50 or more, bronze otherwise. It doesn't compile: each rank only lives inside its own branch. Rewrite it so the if expression hands the String out to one rank variable in main's scope. For the input 120 it prints:
rank: gold
Previous: Stack and heap: where values live Next: Moves: one owner at a time