Skip to content
Learn Rust
Theme

Closures

Working with collections · Lesson 30

Closures are anonymous functions written |args| body. They can use variables from around them.

Coming from Go|x: i32| x * factor is func(x int) int { return x * factor }, usually with the types left out. A closure that changes a captured variable must itself be let mut, and move makes it take ownership of what it uses.

Coming from Kotlin|x| x * factor is { x: Int -> x * factor }. A closure that changes a captured variable must be stored in a let mut, and while it's still going to be used, you can't read that variable yourself.

Coming from PythonA closure is a lambda that can hold a whole block with || { ... }. Python makes you say nonlocal to change a captured variable; Rust makes you say mut: the closure itself must be let mut.

Coming from TypeScript|x| x * factor is the arrow function (x) => x * factor. A closure that changes a captured variable must be let mut, and while it's still going to be used, nothing else can read or write that variable.

Coming from Java|x| x * factor is a lambda, but Rust closures may change captured locals. Java forbids that; the workaround is an AtomicInteger, a one-element array or a field. A closure that changes something must itself be declared let mut.

Coming from C#|x| x * factor is a lambda like x => x * factor, and like C# it can change captured variables. Rust adds two rules: a closure that changes state must be let mut, and while it holds that borrow nobody else can use count.

Coming from CC has no closures; you'd pass a function pointer plus a void * for context. A Rust closure bundles both, captures variables by itself, and is type-checked, so there's no cast from void *.

Coming from C++A closure is a lambda that works out its own capture list: it borrows what it reads, borrows mutably what it changes, and move takes ownership. Rust needs let mut for a closure that changes what it captured even when it changes it through a reference, where a C++ [&] lambda needs nothing.

The quick comparison

Rustsrc/main.rsRun ↗
fn main() {
let factor = 3;
let times = |x: i32| x * factor; // reads factor
println!("{}", times(5));
let mut count = 0;
let mut inc = || count += 1; // changes count, so inc is `mut`
inc();
inc();
println!("count={count}");
}
$ cargo run
15
count=2
package main
import "fmt"
func main() {
factor := 3
times := func(x int) int { return x * factor }
fmt.Println(times(5))
count := 0
inc := func() { count++ }
inc()
inc()
fmt.Printf("count=%d\n", count)
}
$ go run main.go
15
count=2
fun main() {
val factor = 3
val times = { x: Int -> x * factor }
println(times(5))
var count = 0
val inc = { count++ }
inc()
inc()
println("count=$count")
}
$ kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar
15
count=2
factor = 3
times = lambda x: x * factor # reads factor
print(times(5))
count = 0
def inc() -> None:
global count # needed to change a variable from outside
count += 1
inc()
inc()
print(f"count={count}")
$ python3 main.py
15
count=2
const factor = 3;
const times = (x: number) => x * factor; // reads factor
console.log(times(5));
let count = 0;
const inc = () => { count += 1; }; // changes count
inc();
inc();
console.log(`count=${count}`);
$ tsc --strict --module nodenext --types node main.ts && node main.js
15
count=2
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
public class Main {
public static void main(String[] args) {
int factor = 3;
IntUnaryOperator times = x -> x * factor; // reads factor
System.out.println(times.applyAsInt(5));
// a lambda can't reassign a local, so keep the count in an object
var count = new AtomicInteger();
Runnable inc = count::incrementAndGet;
inc.run();
inc.run();
System.out.println("count=" + count);
}
}
$ javac Main.java && java Main
15
count=2
var factor = 3;
var times = (int x) => x * factor; // reads factor
Console.WriteLine(times(5));
var count = 0;
var inc = () => count++; // changes count
inc();
inc();
Console.WriteLine($"count={count}");
$ dotnet run
15
count=2
#include <stdio.h>
// C has no closures. A function cannot capture local variables,
// so you pass what it needs as extra arguments.
int times(int x, int factor) {
return x * factor;
}
void inc(int *count) { // a pointer lets it change the caller's variable
*count += 1;
}
int main(void) {
int factor = 3;
printf("%d\n", times(5, factor));
int count = 0;
inc(&count);
inc(&count);
printf("count=%d\n", count);
return 0;
}
$ gcc -Wall -Wextra main.c -o main && ./main
15
count=2
#include <iostream>
int main() {
int factor = 3;
auto times = [factor](int x) { return x * factor; }; // copies factor in
std::cout << times(5) << "\n";
int count = 0;
auto inc = [&count] { count += 1; }; // & captures count by reference
inc();
inc();
std::cout << "count=" << count << "\n";
}
$ g++ -std=c++20 -Wall -Wextra main.cpp -o main && ./main
15
count=2

What's different

  • A closure borrows what it uses. times holds a &factor, and inc holds a &mut count.
  • While inc is still going to be used, nothing else can touch count. That is the one-writer rule. Reading count works here because inc is never used again after that line.
  • move |x| ... makes the closure take ownership of what it uses instead of borrowing. A closure given to thread::spawn needs it when it uses local variables, because the thread can outlive the function that started it (see Threads).
  • Rust works out from the body whether a closure is Fn (only reads), FnMut (changes something) or FnOnce (gives something away). A function that takes a closure says which kind it accepts.
Gotcha

A changing closure without mut

Looks fine: in Go, Kotlin, TypeScript, C# and C++ it runs fine; in Python it crashes at runtime. Rust won't compile it.

Works in Go. In Rust it's just a rule.Works in Kotlin. In Rust it's just a rule.Broken in Python. Rust catches it.Works in TypeScript. In Rust it's just a rule.Java refuses this too. Same idea, different words.Works in C#. In Rust it's just a rule.No direct equivalent in C. Just a Rust rule.Works in C++. In Rust it's just a rule.
Rustsrc/main.rs✗ won't compile
fn main() {
let mut count = 0;
let inc = || count += 1;
inc();
inc();
println!("count={count}");
}
package main
import "fmt"
func main() {
count := 0
inc := func() { count++ }
inc()
inc()
fmt.Printf("count=%d\n", count)
}
$ go run main.go✓ runs
count=2
fun main() {
var count = 0
val inc = { count++ }
inc()
inc()
println("count=$count")
}
$ kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar✓ runs
count=2
count = 0
def inc():
count += 1
inc()
inc()
print(f"count={count}")
$ python3 main.py! crashes at runtime
Traceback (most recent call last):
  File "main.py", line 8, in <module>
    inc()
    ~~~^^
  File "main.py", line 5, in inc
    count += 1
    ^^^^^
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value
let count = 0;
const inc = () => { count += 1; };
inc();
inc();
console.log(`count=${count}`);
$ tsc --strict --module nodenext --types node main.ts && node main.js✓ runs
count=2
Java✗ refuses too

Java refuses this too, for a different reason: a lambda can't change a local variable it captures. Use an AtomicInteger or a field instead.

public class Main {
public static void main(String[] args) {
int count = 0;
Runnable inc = () -> count += 1;
inc.run();
inc.run();
System.out.println("count=" + count);
}
}
Main.java:4: error: local variables referenced from a lambda expression must be final or effectively final
        Runnable inc = () -> count += 1;
                             ^
1 error
var count = 0;
var inc = () => count += 1;
inc();
inc();
Console.WriteLine($"count={count}");
$ dotnet run✓ runs
count=2
Cno equivalent

C has no closures. A function that changes a counter takes a pointer to it, as in the lesson.

#include <iostream>
int main() {
int count = 0;
auto inc = [&] { count += 1; };
inc();
inc();
std::cout << "count=" << count << "\n";
}
$ g++ -std=c++20 -Wall -Wextra main.cpp -o main && ./main✓ runs
count=2
What does the compiler say? Guess first, then open.
$ cargo buildWhat is E0596?
error[E0596]: cannot borrow `inc` as mutable, as it is not declared as mutable
 --> src/main.rs:3:9
  |
3 |     let inc = || count += 1;
  |         ^^^      ----- calling `inc` requires mutable binding due to mutable borrow of `count`
  |         |
  |         not mutable
4 |     inc();
  |     --- cannot borrow as mutable
5 |     inc();
  |     --- cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
3 |     let mut inc = || count += 1;
  |         +++
What happens in Go

Go runs this and prints count=2. Any closure can change the variables it captured, with nothing to mark.

What happens in Kotlin

Kotlin runs this and prints count=2. A lambda stored in a val can still change a captured var.

What happens in Python

Python raises UnboundLocalError. Assigning to count inside inc makes it a new local variable, so you need global count (or nonlocal in a nested function).

What happens in TypeScript

Prints count=2. A TypeScript arrow function can change outer let variables with no extra marking.

What happens in Java

Java refuses this too, for a different reason: a lambda can't change a local variable it captures. Use an AtomicInteger or a field instead.

What happens in C#

C# lambdas can change captured variables with no extra marker, so this prints count=2.

What happens in C

C has no closures. A function that changes a counter takes a pointer to it, as in the lesson.

What happens in C++

A [&] lambda can change count without any marker, so this prints count=2. Capture by value and C++ also wants a keyword: mutable.

What Rust sees

inc carries a hidden &mut count inside it. Calling inc changes something, so inc itself counts as a value that changes.

Why Rust says no

It is the same rule as in Methods: anything that changes has to be marked mut, so you can spot every place where state changes.

The fix

Compiles and runs.

Rust, fixedsrc/main.rsRun ↗
fn main() {
let mut count = 0;
let mut inc = || count += 1;
inc();
inc();
println!("count={count}");
}
$ cargo run
count=2

The toolkit

Outputs come from Linux on 64-bit ARM, Rust 1.99.1, edition 2024.

A closure is a function written inline as |parameters| body, with no name, that can use the variables around it (the syntax is covered earlier). It is a value: store it, pass it, return it, put it in a struct. Types are usually inferred from the first use.

What a closure captures

Using a variable from outside is called capturing it. Rust picks the least it can get away with, from what the body does:

Rustsrc/main.rsRun ↗
fn main() {
// reads `factor`: the closure holds a shared reference to it
let factor = 3;
let times = |x: i32| x * factor;
println!("times(5): {}", times(5));
println!("factor, read beside the closure: {factor}");
// changes `count`: the closure holds a mutable reference to it
let mut count = 0;
let mut inc = || count += 1;
inc();
inc();
println!("count after two calls: {count}");
// gives `name` away: the closure takes it
let name = String::from("ferris");
let consume = || drop(name);
consume();
println!("the closure has run once; it cannot run again");
}
$ cargo run
times(5): 15
factor, read beside the closure: 3
count after two calls: 2
the closure has run once; it cannot run again
The body… The closure holds Implements Meanwhile the code outside can…
only reads factor a shared reference, &factor Fn, FnMut, FnOnce read it, not change it
changes count a mutable reference, &mut count FnMut, FnOnce not touch it at all
moves name out (drop(name) takes a value and ends it) the value itself FnOnce not use it: it was moved in

These are the ordinary rules of borrowing, mutable borrows and ownership. The closure is a value that holds the references, so the borrow lasts as long as the closure is used. Calling inc changes what it holds, so the variable that holds it must be mut. (Implements is explained below.) Each row has its error:

Rustsrc/main.rs✗ won't compile
fn main() {
let mut x = 1;
let show = || println!("x: {x}");
x = 2;
show();
}
$ cargo buildWhat is E0506?
error[E0506]: cannot assign to `x` because it is borrowed
 --> src/main.rs:4:5
  |
3 |     let show = || println!("x: {x}");
  |                --               - borrow occurs due to use in closure
  |                |
  |                `x` is borrowed here
4 |     x = 2;
  |     ^^^^^ `x` is assigned to here but it was already borrowed
5 |     show();
  |     ---- borrow later used here
Rustsrc/main.rs✗ won't compile
fn main() {
let mut count = 0;
let mut inc = || count += 1;
inc();
println!("count: {count}");
inc();
}
$ cargo buildWhat is E0502?
error[E0502]: cannot borrow `count` as immutable because it is also borrowed as mutable
 --> src/main.rs:5:23
  |
3 |     let mut inc = || count += 1;
  |                   -- ----- first borrow occurs due to use of `count` in closure
  |                   |
  |                   mutable borrow occurs here
4 |     inc();
5 |     println!("count: {count}");
  |                       ^^^^^ immutable borrow occurs here
6 |     inc();
  |     --- mutable borrow later used here
Rustsrc/main.rs✗ won't compile
fn main() {
let name = String::from("ferris");
let consume = || drop(name);
consume();
println!("{name}");
}
$ cargo buildWhat is E0382?
error[E0382]: borrow of moved value: `name`
 --> src/main.rs:5:16
  |
2 |     let name = String::from("ferris");
  |         ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
3 |     let consume = || drop(name);
  |                   --      ---- variable moved due to use in closure
  |                   |
  |                   value moved into closure here
4 |     consume();
5 |     println!("{name}");
  |                ^^^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let consume = || drop(name.clone());
  |                               ++++++++

Reading count after the last call to inc, as the first program does, is fine.

move: take ownership

move before the bars makes the closure take every variable it uses by value, whatever the body does. A number is copied, so the closure gets its own:

Rustsrc/main.rsRun ↗
fn main() {
let mut count = 0;
let mut inc = move || {
count += 1;
count
};
println!("closure's count: {}", inc());
println!("closure's count: {}", inc());
println!("outer count: {count}");
}
$ cargo run
closure's count: 1
closure's count: 2
outer count: 0
let mut inc = move || { count += 1; count }; (after two calls)
STACKcount: i320the outer variable: never changedinc: closurecount: 2its own copy, made when the closure was created
A closure that holds a copy does not see later changes to the original, and the original does not see the closure's.

A String is moved in, so the outside loses it. move does not make a closure run once: this one only reads its String.

Rustsrc/main.rsRun ↗
fn main() {
let name = String::from("ferris");
let greet = move || println!("hello, {name}");
greet();
greet();
}
$ cargo run
hello, ferris
hello, ferris
Rustsrc/main.rs✗ won't compile
fn main() {
let name = String::from("ferris");
let greet = move || println!("hello, {name}");
greet();
println!("{name}");
}
$ cargo buildWhat is E0382?
error[E0382]: borrow of moved value: `name`
 --> src/main.rs:5:16
  |
2 |     let name = String::from("ferris");
  |         ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
3 |     let greet = move || println!("hello, {name}");
  |                 -------                   ---- variable moved due to use in closure
  |                 |
  |                 value moved into closure here
4 |     greet();
5 |     println!("{name}");
  |                ^^^^ value borrowed here after move
  |
help: consider cloning the value before moving it into the closure
  |
3 ~     let value = name.clone();
4 ~     let greet = move || println!("hello, {value}");

A garbage-collected language keeps a captured variable alive as long as any closure uses it. Rust has no collector and frees a function’s locals when it returns, so a closure that outlives its function must own what it uses. That is what move is for. A thread started with thread::spawn may outlive the function that started it, so it cannot borrow that function’s locals (Threads):

Rustsrc/main.rs✗ won't compile
use std::thread;
fn main() {
let name = String::from("ferris");
let handle = thread::spawn(|| println!("hello, {name}"));
handle.join().unwrap();
}
$ cargo buildWhat is E0373?
error[E0373]: closure may outlive the current function, but it borrows `name`, which is owned by the current function
 --> src/main.rs:5:32
  |
5 |     let handle = thread::spawn(|| println!("hello, {name}"));
  |                                ^^                   ---- `name` is borrowed here
  |                                |
  |                                may outlive borrowed value `name`
  |
note: function requires argument type to outlive `'static`
 --> src/main.rs:5:18
  |
5 |     let handle = thread::spawn(|| println!("hello, {name}"));
  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `name` (and any other referenced variables), use the `move` keyword
  |
5 |     let handle = thread::spawn(move || println!("hello, {name}"));
  |                                ++++
Rustsrc/main.rsRun ↗
use std::thread;
fn main() {
let name = String::from("ferris");
let handle = thread::spawn(move || println!("hello from a thread, {name}"));
handle.join().unwrap();
}
$ cargo run
hello from a thread, ferris

The same error appears when a function returns a closure (below); both times the help line suggests move.

Fn, FnMut and FnOnce

The compiler gives every closure some of three traits (a trait is a named ability; traits comes later), as the table’s third column shows. Fn can be called many times, even through a shared reference; FnMut many times from a mut variable; FnOnce once. Which a closure gets depends on what its body does with what it captured, not on move.

You write the traits on the function that receives the closure, as the kind it accepts (impl Fn() means “any closure of that kind”; below has the details):

Rustsrc/main.rsRun ↗
fn call_twice(f: impl Fn()) {
f();
f();
}
fn call_twice_mut(mut f: impl FnMut()) {
f();
f();
}
fn call_once(f: impl FnOnce()) {
f();
}
fn main() {
let name = String::from("ferris");
let mut hits = 0;
// only reads `name`: fits all three
call_twice(|| println!("hello, {name}"));
call_twice_mut(|| println!("hello, {name}"));
call_once(|| println!("hello, {name}"));
// changes `hits`: fits FnMut and FnOnce, not Fn
call_twice_mut(|| hits += 1);
println!("hits: {hits}");
call_once(|| hits += 10);
println!("hits: {hits}");
// gives `name` away: fits only FnOnce
call_once(|| drop(name));
}
$ cargo run
hello, ferris
hello, ferris
hello, ferris
hello, ferris
hello, ferris
hits: 2
hits: 12

The other directions fail. A closure that gives something away can run once:

Rustsrc/main.rs✗ won't compile
fn main() {
let name = String::from("ferris");
let consume = || drop(name);
consume();
consume();
}
$ cargo buildWhat is E0382?
error[E0382]: use of moved value: `consume`
 --> src/main.rs:5:5
  |
4 |     consume();
  |     --------- `consume` moved due to this call
5 |     consume();
  |     ^^^^^^^ value used here after move
  |
note: closure cannot be invoked more than once because it moves the variable `name` out of its environment
 --> src/main.rs:3:27
  |
3 |     let consume = || drop(name);
  |                           ^^^^
note: this value implements `FnOnce`, which causes it to be moved when called
 --> src/main.rs:4:5
  |
4 |     consume();
  |     ^^^^^^^

A function that asks for Fn rejects closures that change or give away what they captured:

Rustsrc/main.rs✗ won't compile
fn call_twice(f: impl Fn()) {
f();
f();
}
fn main() {
let mut count = 0;
call_twice(|| count += 1);
println!("count: {count}");
}
$ cargo buildWhat is E0594?
error[E0594]: cannot assign to `count`, as it is a captured variable in a `Fn` closure
 --> src/main.rs:8:19
  |
1 | fn call_twice(f: impl Fn()) {
  |                  --------- change this to accept `FnMut` instead of `Fn`
...
7 |     let mut count = 0;
  |         --------- `count` declared here, outside the closure
8 |     call_twice(|| count += 1);
  |     ---------- -- ^^^^^^^^^^ cannot assign
  |     |          |
  |     |          in this closure
  |     expects `Fn` instead of `FnMut`
Rustsrc/main.rs✗ won't compile
fn call_twice(f: impl Fn()) {
f();
f();
}
fn main() {
let name = String::from("ferris");
call_twice(|| drop(name));
}
$ cargo buildWhat is E0507?
error[E0507]: cannot move out of `name`, a captured variable in an `Fn` closure
 --> src/main.rs:8:24
  |
7 |     let name = String::from("ferris");
  |         ----   ---------------------- move occurs because `name` has type `String`, which does not implement the `Copy` trait
  |         |
  |         captured outer variable
8 |     call_twice(|| drop(name));
  |                --      ^^^^ `name` is moved here
  |                |
  |                captured by this `Fn` closure
  |
help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but `FnOnce` closures may consume them only once
 --> src/main.rs:1:23
  |
1 | fn call_twice(f: impl Fn()) {
  |                       ^^^^
help: consider cloning the value if the performance cost is acceptable
  |
8 |     call_twice(|| drop(name.clone()));
  |                            ++++++++

Passing a closure to a function

Rustsrc/main.rsRun ↗
fn apply(f: impl Fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn apply_generic<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(x)
}
fn apply_dyn(f: &dyn Fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn apply_ptr(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn repeat(n: u32, mut f: impl FnMut()) {
for _ in 0..n {
f();
}
}
fn double(x: i32) -> i32 {
x * 2
}
fn main() {
let offset = 10;
println!("apply: {}", apply(|x| x + offset, 5));
println!("apply_generic: {}", apply_generic(|x| x + offset, 5));
println!("apply_dyn: {}", apply_dyn(&|x| x + offset, 5));
println!("apply with a named fn: {}", apply(double, 5));
println!("apply_ptr, named fn: {}", apply_ptr(double, 5));
println!(
"apply_ptr, closure with no captures: {}",
apply_ptr(|x| x - 1, 5)
);
let mut calls = 0;
repeat(3, || calls += 1);
println!("repeat(3) made {calls} calls");
repeat(0, || calls += 1);
println!("repeat(0) made {calls} calls in total");
}
$ cargo run
apply: 15
apply_generic: 15
apply_dyn: 15
apply with a named fn: 10
apply_ptr, named fn: 10
apply_ptr, closure with no captures: 4
repeat(3) made 3 calls
repeat(0) made 3 calls in total
  • impl Fn(i32) -> i32 in a parameter means “any type that implements this”. F: Fn(i32) -> i32 with where says the same by naming a type parameter F limited to such closures, (reading signatures, generics).
  • &dyn Fn(i32) -> i32 is one compiled function that takes any closure of that shape through a pointer (trait objects).
  • fn(i32) -> i32 is a plain function pointer. It accepts named functions and closures that capture nothing, and refuses the rest:
Rustsrc/main.rs✗ won't compile
fn apply_ptr(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn main() {
let offset = 10;
println!("{}", apply_ptr(|x| x + offset, 5));
}
$ cargo buildWhat is E0308?
error[E0308]: mismatched types
 --> src/main.rs:7:30
  |
7 |     println!("{}", apply_ptr(|x| x + offset, 5));
  |                    --------- ^^^^^^^^^^^^^^ expected fn pointer, found closure
  |                    |
  |                    arguments to this function are incorrect
  |
  = note: expected fn pointer `fn(i32) -> i32`
                found closure `{closure@src/main.rs:7:30: 7:33}`
note: closures can only be coerced to `fn` types if they do not capture any variables
 --> src/main.rs:7:38
  |
7 |     println!("{}", apply_ptr(|x| x + offset, 5));
  |                                      ^^^^^^ `offset` captured here
note: function defined here
 --> src/main.rs:1:4
  |
1 | fn apply_ptr(f: fn(i32) -> i32, x: i32) -> i32 {
  |    ^^^^^^^^^ -----------------

Ask for the most permissive trait that does the job: FnOnce accepts every closure in the table above, FnMut all but those that give captures away, Fn only those that just read. Ask for FnOnce if you call it once, FnMut if you call it several times (and declare the parameter mut f), and Fn if you must call it through a shared reference, such as &self.

A closure passed by value moves into the function; one that owns a String is not Copy:

Rustsrc/main.rs✗ won't compile
fn call_twice(f: impl Fn()) {
f();
f();
}
fn main() {
let name = String::from("ferris");
let greet = move || println!("hello, {name}");
call_twice(greet);
call_twice(greet);
}
$ cargo buildWhat is E0382?
error[E0382]: use of moved value: `greet`
  --> src/main.rs:10:16
   |
 8 |     let greet = move || println!("hello, {name}");
   |         ----- move occurs because `greet` has type `{closure@src/main.rs:8:17: 8:24}`, which does not implement the `Copy` trait
 9 |     call_twice(greet);
   |                ----- value moved here
10 |     call_twice(greet);
   |                ^^^^^ value used here after move
   |
help: consider borrowing `greet`
   |
 9 |     call_twice(&greet);
   |                +

Lend it instead. A reference to an Fn closure is itself an Fn:

Rustsrc/main.rsRun ↗
fn call_twice(f: impl Fn()) {
f();
f();
}
fn main() {
let name = String::from("ferris");
let greet = move || println!("hello, {name}");
call_twice(&greet); // lend it: `greet` is still ours
call_twice(&greet);
greet();
}
$ cargo run
hello, ferris
hello, ferris
hello, ferris
hello, ferris
hello, ferris

Returning closures

A closure’s type has no name you can write, so a function returns it as impl Fn(...), “some one type that implements this”.

Rustsrc/main.rsRun ↗
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n
}
fn make_counter() -> impl FnMut() -> u32 {
let mut count = 0;
move || {
count += 1;
count
}
}
fn pick(op: &str) -> Box<dyn Fn(i32, i32) -> i32> {
match op {
"add" => Box::new(|a, b| a + b),
"mul" => Box::new(|a, b| a * b),
_ => Box::new(|_, _| 0),
}
}
fn main() {
let add5 = make_adder(5);
let add10 = make_adder(10);
println!("add5(1): {}, add10(1): {}", add5(1), add10(1));
let mut a = make_counter();
let mut b = make_counter();
println!("a: {} {}, b: {}", a(), a(), b());
for op in ["add", "mul", "other"] {
println!("{op}(3, 4): {}", pick(op)(3, 4));
}
}
$ cargo run
add5(1): 6, add10(1): 11
a: 1 2, b: 1
add(3, 4): 7
mul(3, 4): 12
other(3, 4): 0

The closure must own what it uses, because the function’s locals are gone when it returns. Without move:

Rustsrc/main.rs✗ won't compile
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
|x| x + n
}
fn main() {
println!("{}", make_adder(5)(1));
}
$ cargo buildWhat is E0373?
error[E0373]: closure may outlive the current function, but it borrows `n`, which is owned by the current function
 --> src/main.rs:2:5
  |
2 |     |x| x + n
  |     ^^^     - `n` is borrowed here
  |     |
  |     may outlive borrowed value `n`
  |
note: closure is returned here
 --> src/main.rs:2:5
  |
2 |     |x| x + n
  |     ^^^^^^^^^
help: to force the closure to take ownership of `n` (and any other referenced variables), use the `move` keyword
  |
2 |     move |x| x + n
  |     ++++

Each call to make_counter makes a closure with its own count. impl Fn names one type and every closure expression has its own, so two closures written separately cannot come out of one if (unless neither captures anything, below):

Rustsrc/main.rs✗ won't compile
fn pick(add: bool, n: i32) -> impl Fn(i32) -> i32 {
if add { move |x| x + n } else { move |x| x * n }
}
fn main() {
println!("{}", pick(true, 5)(1));
}
$ cargo buildWhat is E0308?
error[E0308]: `if` and `else` have incompatible types
 --> src/main.rs:2:38
  |
2 |     if add { move |x| x + n } else { move |x| x * n }
  |              --------------          ^^^^^^^^^^^^^^ expected closure, found a different closure
  |              |
  |              the expected closure
  |              expected because of this
  |
  = note: expected closure `{closure@src/main.rs:2:14: 2:22}`
             found closure `{closure@src/main.rs:2:38: 2:46}`
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object
help: you could change the return type to be a boxed trait object
  |
1 - fn pick(add: bool, n: i32) -> impl Fn(i32) -> i32 {
1 + fn pick(add: bool, n: i32) -> Box<dyn Fn(i32) -> i32> {
  |
help: if you change the return type to expect trait objects, box the returned expressions
  |
2 |     if add { Box::new(move |x| x + n) } else { Box::new(move |x| x * n) }
  |              +++++++++              +          +++++++++              +

The fix is Box<dyn Fn(...)>, as pick does. Box<T> is an owned pointer to a value on the heap (Smart pointers). dyn Fn(i32, i32) -> i32 means “some closure with this signature, found at run time” (trait objects). One type then holds any closure of that shape, which a Vec of different closures also needs. A stored boxed closure must own what it uses, written 'static (lifetimes); the registry exercise meets it.

Closures that capture nothing are the exception. Each coerces to a fn pointer, so they can share a type:

Rustsrc/main.rsRun ↗
// Neither closure captures anything, so each one coerces to a plain `fn` pointer
// and the two arms have the same type.
fn pick(add: bool) -> fn(i32) -> i32 {
if add { |x| x + 1 } else { |x| x * 2 }
}
fn main() {
println!("pick(true)(5): {}", pick(true)(5));
println!("pick(false)(5): {}", pick(false)(5));
let both = [|x: i32| x + 1, |x: i32| x * 2];
println!("both[1](5): {}", both[1](5));
}
$ cargo run
pick(true)(5): 6
pick(false)(5): 10
both[1](5): 10

Closures in structs, and callbacks

A struct holds a closure through a type parameter (F below: a placeholder the compiler fills in with the closure’s type; generics) when it has one, or through Box<dyn FnMut> (or Box<dyn Fn>) when it has several of different types:

Rustsrc/main.rsRun ↗
// One closure type per struct: the type parameter `F` is that closure's type.
struct Button<F: Fn(&str)> {
label: String,
on_click: F,
}
impl<F: Fn(&str)> Button<F> {
fn click(&self) {
(self.on_click)(&self.label);
}
}
// Many closures of different types: box each one.
struct Events {
handlers: Vec<Box<dyn FnMut(&str) -> usize>>,
}
impl Events {
fn fire(&mut self, event: &str) -> Vec<usize> {
self.handlers.iter_mut().map(|h| h(event)).collect()
}
}
fn main() {
let prefix = String::from("clicked");
let button = Button {
label: String::from("OK"),
on_click: |label| println!("{prefix}: {label}"),
};
button.click();
button.click();
let mut seen = 0;
let mut events = Events {
handlers: Vec::new(),
};
events.handlers.push(Box::new(|e| e.len()));
events.handlers.push(Box::new(move |_| {
seen += 1;
seen
}));
println!("fire 1: {:?}", events.fire("start"));
println!("fire 2: {:?}", events.fire("stop"));
println!("outer seen (the closure counts its own copy): {seen}");
}
$ cargo run
clicked: OK
clicked: OK
fire 1: [5, 1]
fire 2: [4, 2]
outer seen (the closure counts its own copy): 0

A closure in a field is called with parentheses around the field, (self.on_click)(...). Without them Rust looks for a method:

Rustsrc/main.rs✗ won't compile
struct Button<F: Fn(&str)> {
on_click: F,
}
fn main() {
let button = Button {
on_click: |label| println!("clicked {label}"),
};
button.on_click("OK");
}
$ cargo buildWhat is E0599?
error[E0599]: no method named `on_click` found for struct `Button<F>` in the current scope
 --> src/main.rs:9:12
  |
1 | struct Button<F: Fn(&str)> {
  | -------------------------- method `on_click` not found for this struct
...
9 |     button.on_click("OK");
  |            ^^^^^^^^ field, not a method
  |
help: to call the closure stored in `on_click`, surround the field access with parentheses
  |
9 |     (button.on_click)("OK");
  |     +               +

Box<dyn FnOnce() -> String> holds a job that runs once (thread::spawn takes an FnOnce too). Looping over the Vec by value gives each job away as it is run:

Rustsrc/main.rsRun ↗
fn main() {
let name = String::from("ferris");
let mut jobs: Vec<Box<dyn FnOnce() -> String>> = Vec::new();
jobs.push(Box::new(move || name)); // hands `name` back: it can run only once
jobs.push(Box::new(|| String::from("second job")));
for job in jobs {
println!("job returned: {}", job());
}
}
$ cargo run
job returned: ferris
job returned: second job

In a loop, each pass has its own i, which ends with the pass, so the closure must own a copy:

Rustsrc/main.rsRun ↗
fn main() {
let mut fs: Vec<Box<dyn Fn() -> i32>> = Vec::new();
for i in 0..3 {
fs.push(Box::new(move || i));
}
for f in &fs {
print!("{} ", f());
}
println!();
}
$ cargo run
0 1 2 
Rustsrc/main.rs✗ won't compile
fn main() {
let mut fs: Vec<Box<dyn Fn() -> i32>> = Vec::new();
for i in 0..3 {
fs.push(Box::new(|| i));
}
for f in &fs {
print!("{} ", f());
}
println!();
}
$ cargo buildWhat is E0597?
error[E0597]: `i` does not live long enough
 --> src/main.rs:4:29
  |
3 |     for i in 0..3 {
  |         - binding `i` declared here
4 |         fs.push(Box::new(|| i));
  |         --               -- ^ borrowed value does not live long enough
  |         |                |
  |         |                value captured here
  |         borrow later used here
5 |     }
  |     - `i` dropped here while still borrowed

What a closure cannot do

Its parameter types are fixed by the first use. A closure is not generic like a function:

Rustsrc/main.rs✗ won't compile
fn main() {
let identity = |x| x;
println!("{}", identity(1));
println!("{}", identity("one"));
}
$ cargo buildWhat is E0308?
error[E0308]: mismatched types
 --> src/main.rs:4:29
  |
4 |     println!("{}", identity("one"));
  |                    -------- ^^^^^ expected integer, found `&str`
  |                    |
  |                    arguments to this function are incorrect
  |
note: expected because the closure was earlier called with an argument of type `{integer}`
 --> src/main.rs:3:29
  |
3 |     println!("{}", identity(1));
  |                    -------- ^ expected because this argument is of type `{integer}`
  |                    |
  |                    in this closure call
note: closure parameter defined here
 --> src/main.rs:2:21
  |
2 |     let identity = |x| x;
  |                     ^

It cannot call itself by name. The name factorial does not exist until the let is finished, and the closure has no name of its own. Write a recursive helper as a nested fn, which can see its own name and captures nothing:

Rustsrc/main.rs✗ won't compile
fn main() {
let factorial = |n: u64| if n <= 1 { 1 } else { n * factorial(n - 1) };
println!("{}", factorial(5));
}
$ cargo buildWhat is E0425?
error[E0425]: cannot find function `factorial` in this scope
 --> src/main.rs:2:57
  |
2 |     let factorial = |n: u64| if n <= 1 { 1 } else { n * factorial(n - 1) };
  |                                                         ^^^^^^^^^ not found in this scope
Rustsrc/main.rsRun ↗
fn main() {
// A function item has a name, so it can call itself.
fn factorial(n: u64) -> u64 {
if n <= 1 { 1 } else { n * factorial(n - 1) }
}
println!("factorial(5): {}", factorial(5));
}
$ cargo run
factorial(5): 120

Closures in the standard library

Closures are how you hand a rule to a method: sort_by_key takes one that works out a key, sort_by one that compares two items, retain one that says whether to keep an item. Iterator adapters such as map and filter take closures the same way.

Rustsrc/main.rsRun ↗
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn person(name: &str, age: u32) -> Person {
Person {
name: name.to_string(),
age,
}
}
fn main() {
let mut people = vec![person("Cy", 40), person("Ana", 31), person("Bo", 25)];
people.sort_by_key(|p| p.age);
println!(
"by age: {:?}",
people.iter().map(|p| &p.name).collect::<Vec<_>>()
);
people.sort_by(|a, b| a.name.cmp(&b.name));
println!(
"by name: {:?}",
people.iter().map(|p| &p.name).collect::<Vec<_>>()
);
let min_age = 30;
people.retain(|p| p.age >= min_age);
println!(
"aged {min_age}+: {:?}",
people.iter().map(|p| &p.name).collect::<Vec<_>>()
);
let total: u32 = people.iter().map(|p| p.age).sum();
println!("total age: {total}");
}
$ cargo run
by age: ["Bo", "Ana", "Cy"]
by name: ["Ana", "Bo", "Cy"]
aged 30+: ["Ana", "Cy"]
total age: 71

sort_by_key has a trap: the key may not borrow from the item.

Rustsrc/main.rs✗ won't compile
struct Person {
name: String,
}
fn main() {
let mut people = vec![
Person {
name: String::from("Cy"),
},
Person {
name: String::from("Ana"),
},
];
people.sort_by_key(|p| &p.name);
println!("{}", people[0].name);
}
$ cargo build
error: lifetime may not live long enough
  --> src/main.rs:14:28
   |
14 |     people.sort_by_key(|p| &p.name);
   |                         -- ^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                         ||
   |                         |return type of closure is &'2 String
   |                         has type `&'1 Person`

Use sort_by(|a, b| a.name.cmp(&b.name)), as above, or return a copy such as p.name.clone().

A closure also delays work. unwrap_or(x) computes x before the call, whether or not it is used. unwrap_or_else(f) runs f only for None (ok_or_else and or_insert_with follow the same pattern):

Rustsrc/main.rsRun ↗
fn default_port() -> u16 {
println!(" (default_port ran)");
8080
}
fn main() {
let configured: Option<u16> = Some(443);
println!("unwrap_or:");
let a = configured.unwrap_or(default_port());
println!(" -> {a}");
println!("unwrap_or_else:");
let b = configured.unwrap_or_else(default_port);
println!(" -> {b}");
println!("unwrap_or_else with nothing configured:");
let missing: Option<u16> = None;
let c = missing.unwrap_or_else(|| default_port() + 1);
println!(" -> {c}");
}
$ cargo run
unwrap_or:
  (default_port ran)
  -> 443
unwrap_or_else:
  -> 443
unwrap_or_else with nothing configured:
  (default_port ran)
  -> 8081

Which form to write

You are… Write
handing a rule to a method you call right now (sort_by_key, retain, map) a plain closure; no move
a thread::spawn thread, a returned closure, or one kept after the function ends move |args| ...
a function that takes a callback the trait that matches how you call it (above)
one closure in a struct; several different ones a type parameter limited to Fn(..); Box<dyn Fn(..)> in a Vec

When the compiler complains about a closure, the message points at the capture (x is borrowed here, value moved into closure here) and usually offers move or FnMut. Read which variable it names first: move on a closure that should share a counter gives it a private copy.

Your closures in Rust

Going deeper

A closure is a struct

The compiler turns a closure into a value that holds its captures. Sizes show what it holds:

Rustsrc/main.rsRun ↗
use std::mem::{size_of, size_of_val};
fn main() {
let factor = 3;
let mut count = 0;
let name = String::from("ferris");
println!("name: len {}, capacity {}", name.len(), name.capacity());
let no_captures = |x: i32| x + 1;
let reads_factor = |x: i32| x * factor;
let bumps_count = || count += 1;
let owns_factor = move |x: i32| x * factor;
let owns_name = move || name.len();
println!(
"size of a closure that captures nothing: {}",
size_of_val(&no_captures)
);
println!(
"reads factor (shared ref): {}",
size_of_val(&reads_factor)
);
println!(
"bumps count (mutable ref): {}",
size_of_val(&bumps_count)
);
println!(
"owns factor (an i32): {}",
size_of_val(&owns_factor)
);
println!("owns name (a String): {}", size_of_val(&owns_name));
println!(
"for comparison, &i32: {}, i32: {}, String: {}",
size_of::<&i32>(),
size_of::<i32>(),
size_of::<String>()
);
}
$ cargo run
name: len 6, capacity 6
size of a closure that captures nothing: 0
reads factor (shared ref):    8
bumps count (mutable ref):    8
owns factor (an i32):         4
owns name (a String):         24
for comparison, &i32: 8, i32: 4, String: 24

A closure that captures nothing takes no space. One that holds a reference is the size of a reference, and one that owns a String is the size of a String. (The layout of closures is not promised; these are what this compiler prints.)

three closures, three kinds of capture
STACKHEAPfactor: i323reads_factor: closure&factor8 bytes: a shared referencecount: i320bumps_count: closure&mut count8 bytes: a mutable referencename: String (moved)ptrlen 6cap 6owns_name: closure (move)ptrlen 6cap 624 bytes: the String itself"ferris"ferris
The closure is on the stack, like any struct. Only the String's own text is on the heap. (The order of a String's fields is a convenient choice.)

Every closure expression has its own type, even two identical ones, which is why separately written closures need Box<dyn Fn> to share a Vec.

Captures are exact

A closure captures the part of a variable it names. p.name borrows the field name, and the other fields stay free. This applies from the 2021 edition on (the runner builds edition 2024); in the 2018 edition p.score += 5 below is an error (E0506). The Edition Guide describes the change.

Rustsrc/main.rsRun ↗
struct Player {
name: String,
score: u32,
}
fn main() {
let mut p = Player {
name: String::from("Ana"),
score: 0,
};
// The closure uses only `p.name`, so it borrows only that field.
let show = || println!("player {}", p.name);
p.score += 5; // a different field: allowed while `show` is alive
show();
println!("score: {}", p.score);
}
$ cargo run
player Ana
score: 5

Calling a method on the variable names all of it, because the method takes &self:

Rustsrc/main.rs✗ won't compile
struct Player {
name: String,
score: u32,
}
impl Player {
fn label(&self) -> String {
format!("player {}", self.name)
}
}
fn main() {
let mut p = Player {
name: String::from("Ana"),
score: 0,
};
let show = || println!("{}", p.label());
p.score += 5;
show();
}
$ cargo buildWhat is E0506?
error[E0506]: cannot assign to `p.score` because it is borrowed
  --> src/main.rs:19:5
   |
18 |     let show = || println!("{}", p.label());
   |                --                - borrow occurs due to use in closure
   |                |
   |                `p.score` is borrowed here
19 |     p.score += 5;
   |     ^^^^^^^^^^^^ `p.score` is assigned to here but it was already borrowed
20 |     show();
   |     ---- borrow later used here

Generic, dyn, or fn pointer

An impl Fn or F: Fn parameter is filled in with the closure’s own type, so the compiler knows which code each call runs (generics). A dyn Fn value is a pointer to the closure plus a pointer to a table of its methods, and a call goes through the table, so the compiler cannot always see which code runs (an optimised build sometimes can). The sizes show the two pointers; a fn pointer is one:

Rustsrc/main.rsRun ↗
use std::mem::size_of;
fn main() {
println!(
"&dyn Fn(i32) -> i32: {}",
size_of::<&dyn Fn(i32) -> i32>()
);
println!(
"Box<dyn Fn(i32) -> i32>: {}",
size_of::<Box<dyn Fn(i32) -> i32>>()
);
println!("fn(i32) -> i32: {}", size_of::<fn(i32) -> i32>());
println!("&i32 (for comparison): {}", size_of::<&i32>());
}
$ cargo run
&dyn Fn(i32) -> i32:      16
Box<dyn Fn(i32) -> i32>:  16
fn(i32) -> i32:           8
&i32 (for comparison):    8

Reach for dyn when you store closures of different types together or choose one at run time; otherwise prefer impl Fn. No timings are claimed here: measure your own case.

Practice

Closures that return closures

Write make_adder, make_counter and compose. Each returns a closure that outlives the function, so each needs move: forgetting it is the error you are meant to meet. The checks interleave two counters (a static total fails) and compose in both orders.

Starterexercises/compose.rs
// Three functions that return a closure. Each returned closure must keep working after
// the function that made it has returned, so it has to own what it uses.
// A closure that adds `n` to its argument.
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
|_x| todo!("return a closure that adds `n`; it uses `n` after this function is gone")
}
// A closure that returns a running total: each call adds `step` and returns the total.
// Every counter made by this function has its own total.
fn make_counter(step: i32) -> impl FnMut() -> i32 {
|| todo!("return a closure with its own `total`; each call adds `step` and returns it")
}
// A closure that applies `first` to its argument and then `second` to the result.
fn compose(first: impl Fn(i32) -> i32, second: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
|_x| todo!("return a closure that calls both; it must own `first` and `second`")
}
fn main() {
let add2 = make_adder(2);
let add10 = make_adder(10);
assert_eq!(add2(1), 3);
assert_eq!(add10(1), 11);
// Two counters made by the same function do not share a total, even when their
// calls are interleaved.
let mut by_one = make_counter(1);
let mut by_five = make_counter(5);
assert_eq!(by_one(), 1);
assert_eq!(by_one(), 2);
assert_eq!(by_five(), 5);
assert_eq!(by_one(), 3);
assert_eq!(by_five(), 10);
let add_then_double = compose(make_adder(1), |x| x * 2);
let double_then_add = compose(|x| x * 2, make_adder(1));
assert_eq!(add_then_double(5), 12);
assert_eq!(double_then_add(5), 11);
// A composed closure can be composed again.
let add3 = compose(make_adder(1), make_adder(2));
let add_then_double_then_negate = compose(add_then_double, |x| -x);
assert_eq!(add3(0), 3);
assert_eq!(add_then_double_then_negate(5), -12);
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/compose.solution.rsRun ↗
// Three functions that return a closure. Each returned closure must keep working after
// the function that made it has returned, so it has to own what it uses.
// A closure that adds `n` to its argument.
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n
}
// A closure that returns a running total: each call adds `step` and returns the total.
// Every counter made by this function has its own total.
fn make_counter(step: i32) -> impl FnMut() -> i32 {
let mut total = 0;
move || {
total += step;
total
}
}
// A closure that applies `first` to its argument and then `second` to the result.
fn compose(first: impl Fn(i32) -> i32, second: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
move |x| second(first(x))
}
fn main() {
let add2 = make_adder(2);
let add10 = make_adder(10);
assert_eq!(add2(1), 3);
assert_eq!(add10(1), 11);
// Two counters made by the same function do not share a total, even when their
// calls are interleaved.
let mut by_one = make_counter(1);
let mut by_five = make_counter(5);
assert_eq!(by_one(), 1);
assert_eq!(by_one(), 2);
assert_eq!(by_five(), 5);
assert_eq!(by_one(), 3);
assert_eq!(by_five(), 10);
let add_then_double = compose(make_adder(1), |x| x * 2);
let double_then_add = compose(|x| x * 2, make_adder(1));
assert_eq!(add_then_double(5), 12);
assert_eq!(double_then_add(5), 11);
// A composed closure can be composed again.
let add3 = compose(make_adder(1), make_adder(2));
let add_then_double_then_negate = compose(add_then_double, |x| -x);
assert_eq!(add3(0), 3);
assert_eq!(add_then_double_then_negate(5), -12);
println!("all checks passed");
}
$ cargo run
all checks passed

Fn, FnMut or FnOnce?

Three functions call a closure: once, several times, and through &self. The starter declares every parameter FnOnce, so it compiles. Write the bodies and pick the most permissive trait that works for each.

Starterexercises/callbacks.rs
// Three functions that call a closure they are given. Every parameter below is
// `FnOnce`, because `FnOnce` accepts every closure and so the starter compiles.
// Write the body of each function, read the compiler's complaint, and change the trait
// to the most permissive one that works: `FnOnce` if the function calls it once,
// `FnMut` if it calls it several times, `Fn` if it calls it through a shared reference.
// Calls `job` once and returns what it gives back.
fn run_once(job: impl FnOnce() -> String) -> String {
todo!("call job once and return its result")
}
// Calls `tick` `times` times.
fn repeat(times: u32, tick: impl FnOnce()) {
todo!("call tick `times` times")
}
struct Button<F> {
on_click: F,
}
impl<F: FnOnce() -> i32> Button<F> {
// Takes `&self`, so it can be called any number of times on a shared button.
fn click(&self) -> i32 {
todo!("call on_click through &self")
}
}
fn main() {
// This closure hands `name` over, so it can only run once.
let name = String::from("ferris");
assert_eq!(run_once(move || name), "ferris");
// This one changes `total`, so it runs many times but needs `mut` access.
let mut total = 0;
repeat(3, || total += 2);
assert_eq!(total, 6);
repeat(0, || total += 100);
assert_eq!(total, 6);
// This one only reads `limit`, so a shared button can call it again and again.
let limit = 10;
let button = Button {
on_click: || limit * 2,
};
assert_eq!(button.click(), 20);
assert_eq!(button.click(), 20);
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/callbacks.solution.rsRun ↗
// Three functions that call a closure they are given. Pick the trait each parameter
// needs: `FnOnce` if the function calls it once, `FnMut` if it calls it several times,
// `Fn` if it calls it through a shared reference (`&self`).
// Calls `job` once and returns what it gives back.
fn run_once(job: impl FnOnce() -> String) -> String {
job()
}
// Calls `tick` `times` times.
fn repeat(times: u32, mut tick: impl FnMut()) {
for _ in 0..times {
tick();
}
}
struct Button<F> {
on_click: F,
}
impl<F: Fn() -> i32> Button<F> {
// Takes `&self`, so it can be called any number of times on a shared button.
fn click(&self) -> i32 {
(self.on_click)()
}
}
fn main() {
// This closure hands `name` over, so it can only run once.
let name = String::from("ferris");
assert_eq!(run_once(move || name), "ferris");
// This one changes `total`, so it runs many times but needs `mut` access.
let mut total = 0;
repeat(3, || total += 2);
assert_eq!(total, 6);
repeat(0, || total += 100);
assert_eq!(total, 6);
// This one only reads `limit`, so a shared button can call it again and again.
let limit = 10;
let button = Button {
on_click: || limit * 2,
};
assert_eq!(button.click(), 20);
assert_eq!(button.click(), 20);
println!("all checks passed");
}
$ cargo run
all checks passed

A closure that uses a local

Write closest: retain the words that are not banned, then sort_by_key by how far each length is from target, ties alphabetical. Both closures use a local variable, as most closures you write will. The key may not borrow from the item.

Starterexercises/closest.rs
// Most closures you write call a method that takes one: `retain`, `sort_by_key`.
// Here both use a local variable from the function around them.
// Removes the words in `banned`, then sorts the rest by how far their length (in
// bytes) is from `target`, nearest first. Words the same distance away stay in
// alphabetical order.
fn closest(words: &mut Vec<String>, target: usize, banned: &[&str]) {
todo!("retain the words that are not banned, then sort_by_key with a key that uses `target`")
}
fn main() {
let mut words: Vec<String> = ["pear", "fig", "banana", "kiwi", "apple", "plum"]
.iter()
.map(|w| w.to_string())
.collect();
closest(&mut words, 4, &["apple"]);
// distance 0: kiwi, pear, plum (alphabetical); distance 1: fig; distance 2: banana
assert_eq!(words, ["kiwi", "pear", "plum", "fig", "banana"]);
// A different target changes the order; a banned word that is not there is fine.
closest(&mut words, 6, &["grape"]);
assert_eq!(words, ["banana", "kiwi", "pear", "plum", "fig"]);
// Nothing left, nothing to sort.
let mut few = vec![String::from("fig")];
closest(&mut few, 3, &["fig"]);
assert!(few.is_empty());
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/closest.solution.rsRun ↗
// Most closures you write call a method that takes one: `retain`, `sort_by_key`.
// Here both use a local variable from the function around them.
// Removes the words in `banned`, then sorts the rest by how far their length (in
// bytes) is from `target`, nearest first. Words the same distance away stay in
// alphabetical order.
fn closest(words: &mut Vec<String>, target: usize, banned: &[&str]) {
words.retain(|w| !banned.contains(&w.as_str()));
words.sort_by_key(|w| (w.len().abs_diff(target), w.clone()));
}
fn main() {
let mut words: Vec<String> = ["pear", "fig", "banana", "kiwi", "apple", "plum"]
.iter()
.map(|w| w.to_string())
.collect();
closest(&mut words, 4, &["apple"]);
// distance 0: kiwi, pear, plum (alphabetical); distance 1: fig; distance 2: banana
assert_eq!(words, ["kiwi", "pear", "plum", "fig", "banana"]);
// A different target changes the order; a banned word that is not there is fine.
closest(&mut words, 6, &["grape"]);
assert_eq!(words, ["banana", "kiwi", "pear", "plum", "fig"]);
// Nothing left, nothing to sort.
let mut few = vec![String::from("fig")];
closest(&mut few, 3, &["fig"]);
assert!(few.is_empty());
println!("all checks passed");
}
$ cargo run
all checks passed

A registry of callbacks

Store closures of different types in one Vec<Box<dyn Fn>>, then call those registered under a name, in order. The checks include an unknown name and names that share a prefix with a registered one: only an exact name matches.

Starterexercises/registry.rs
// A registry of named callbacks. `on` stores a handler under a name; `fire` calls every
// handler stored under that name, in the order they were added, and collects what they
// return. A name with no handlers gives an empty Vec.
struct Registry {
handlers: Vec<(String, Box<dyn Fn(&str) -> String>)>,
}
impl Registry {
fn new() -> Registry {
Registry {
handlers: Vec::new(),
}
}
// `'static` means the closure may not borrow local variables: it must own what it uses.
fn on(&mut self, name: &str, handler: impl Fn(&str) -> String + 'static) {
todo!("box the handler and push it with its name")
}
fn fire(&self, name: &str, arg: &str) -> Vec<String> {
todo!("call each handler whose name matches, in order")
}
}
fn main() {
let mut registry = Registry::new();
let prefix = String::from("hello");
registry.on("greet", move |who| format!("{prefix}, {who}"));
registry.on("greet", |who| who.to_uppercase());
registry.on("count", |text| text.len().to_string());
registry.on("greeting", |_| String::from("not a greet"));
registry.on("", |_| String::from("empty name"));
assert_eq!(registry.fire("greet", "ana"), ["hello, ana", "ANA"]);
assert_eq!(registry.fire("count", "hello"), ["5"]);
assert!(registry.fire("missing", "x").is_empty());
// Only an exact name matches: "greet" is a prefix of "greeting", "count" is not.
assert_eq!(registry.fire("greeting", "x"), ["not a greet"]);
assert_eq!(registry.fire("gree", "x"), Vec::<String>::new());
assert_eq!(registry.fire("", "x"), ["empty name"]);
let empty = Registry::new();
assert!(empty.fire("greet", "x").is_empty());
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/registry.solution.rsRun ↗
// A registry of named callbacks. `on` stores a handler under a name; `fire` calls every
// handler stored under that name, in the order they were added, and collects what they
// return. A name with no handlers gives an empty Vec.
struct Registry {
handlers: Vec<(String, Box<dyn Fn(&str) -> String>)>,
}
impl Registry {
fn new() -> Registry {
Registry {
handlers: Vec::new(),
}
}
// `'static` means the closure may not borrow local variables: it must own what it uses.
fn on(&mut self, name: &str, handler: impl Fn(&str) -> String + 'static) {
self.handlers.push((name.to_string(), Box::new(handler)));
}
fn fire(&self, name: &str, arg: &str) -> Vec<String> {
let mut results = Vec::new();
for (registered, handler) in &self.handlers {
if registered == name {
results.push(handler(arg));
}
}
results
}
}
fn main() {
let mut registry = Registry::new();
let prefix = String::from("hello");
registry.on("greet", move |who| format!("{prefix}, {who}"));
registry.on("greet", |who| who.to_uppercase());
registry.on("count", |text| text.len().to_string());
registry.on("greeting", |_| String::from("not a greet"));
registry.on("", |_| String::from("empty name"));
assert_eq!(registry.fire("greet", "ana"), ["hello, ana", "ANA"]);
assert_eq!(registry.fire("count", "hello"), ["5"]);
assert!(registry.fire("missing", "x").is_empty());
// Only an exact name matches: "greet" is a prefix of "greeting", "count" is not.
assert_eq!(registry.fire("greeting", "x"), ["not a greet"]);
assert_eq!(registry.fire("gree", "x"), Vec::<String>::new());
assert_eq!(registry.fire("", "x"), ["empty name"]);
let empty = Registry::new();
assert!(empty.fire("greet", "x").is_empty());
println!("all checks passed");
}
$ cargo run
all checks passed

Read more