Step 2 of 6
Capturing variables from the scope
Here is what makes closures more than short functions: a closure can use the variables of the scope it's written in. The closure captures them.
fn main() {
let tax_rate = 0.25;
let with_tax = |price: f64| price * (1.0 + tax_rate);
println!("{:.2}", with_tax(8.0));
println!("{:.2}", with_tax(20.0));
println!("rate is still {tax_rate}");
}
10.00
25.00
rate is still 0.25
with_tax uses tax_rate without taking it as a parameter. A fn can't do that, even one written inside main:
fn main() {
let tax_rate = 0.25;
fn with_tax(price: f64) -> f64 {
price * (1.0 + tax_rate)
}
println!("{:.2}", with_tax(8.0));
}
error[E0434]: can't capture dynamic environment in a fn item
The compiler even suggests the fix: help: use the `|| { ... }` closure form instead.
Three ways to capture
The compiler looks at what the body does with each variable and captures it in the gentlest way that works:
| The body... | The closure captures | Like writing |
|---|---|---|
only reads x |
a shared borrow | &x |
changes x |
a mutable borrow | &mut x |
takes ownership of x (moves it somewhere) |
the value itself | a move of x |
All the borrowing rules you know apply, with the closure as the borrower. A closure that changes a captured variable must itself be declared mut, because calling it changes something:
fn main() {
let mut count = 0;
let mut tick = || count += 1;
tick();
tick();
tick();
println!("ticks: {count}");
}
ticks: 3
The mutable borrow of count starts where tick is created and lasts until the last call of tick. After that, count is free again, so the println! is fine.
Common mistakes
- Forgetting
muton the closure. Calling a closure that changes what it captured needs mutable access to the closure:
fn main() {
let mut count = 0;
let tick = || count += 1;
tick();
println!("{count}");
}
error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable
- Using the variable while the closure still borrows it. Between creating
tickand its last call,countis mutably borrowed, so you can't even read it:
fn main() {
let mut count = 0;
let mut tick = || count += 1;
tick();
println!("{count}");
tick();
}
error[E0502]: cannot borrow `count` as immutable because it is also borrowed as mutable
Fix it by finishing with the closure first, or by passing the value in as a parameter instead of capturing it.
Your task
The program has a bonus and a score. Write a closure add_points that takes a number of points (i32) and adds points + bonus to score. Call it with 10 and then with 20, so the program prints score: 40.
Challenge 2
A closure can capture a Vec mutably too. Write a closure log that takes a message (&str) and pushes a numbered line onto lines: the first message becomes "1. server started", the second "2. ...", and so on (the number is lines.len() + 1). Log the three messages server started, user ann logged in and disk 91% full, in that order. The loop at the end prints them:
1. server started
2. user ann logged in
3. disk 91% full
Challenge 3
The starter code doesn't compile: the first println! reads visits while the closure visit still borrows it mutably (it's called again later). Fix it by reordering lines, not by adding .clone(), so the program prints:
visited: ["hall", "kitchen", "garden"]
count: 3