Rust Returning Closures
A returned closure is a closure that a function hands back to its caller. The caller keeps the closure and runs it later.
Return a closure as impl Fn
A closure has a hidden type with no name you can write, so the function promises a kind instead. move makes the returned closure own everything it captures:
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n
}
let add5 = make_adder(5);
println!("{}", add5(10));An FnMut return lives in a mut variable
Calling this closure changes what it captured, so the variable that stores it must allow change:
fn make_shouter() -> impl FnMut() -> String {
let mut bangs = String::new();
move || {
bangs.push('!');
format!("go{bangs}")
}
}
let mut shout = make_shouter();
println!("{} {}", shout(), shout());An FnOnce return runs exactly once
This closure hands its capture out when called, so the call uses it up — once is all it has:
fn make_ticket() -> impl FnOnce() -> String {
let seat = String::from("7A");
move || format!("seat {seat}")
}
let ticket = make_ticket();
println!("{}", ticket());Example
fn make_greeter() -> impl Fn() {
let word = String::from("hello");
move || println!("{word}, learner!")
}
fn make_counter() -> impl FnMut() -> i32 {
let mut count = 0;
move || {
count += 1;
count
}
}
fn make_message() -> impl FnOnce() -> String {
let text = String::from("one time only");
move || text
}
fn main() {
let greet = make_greeter();
greet();
greet();
let mut counter = make_counter();
println!("count = {}", counter());
println!("count = {}", counter());
let once = make_message();
println!("message = {}", once());
}Key points
- A closure that only borrowed a local variable could not be returned.
- The local is dropped when the function ends.
- The borrow would point at nothing.
- One function returns one fixed hidden type.
- Two different closures cannot take turns being the return value.
- Different closures cannot share one Vec as impl Fn.
- make_counter() hands out a fresh counter every call.
- Each counter has its own private count inside.
- The closure is a piece of behaviour, just as a returned number is a piece of data.
- A returned closure hands behaviour back to the caller to run later.
In this example, make_greeter builds the String word inside the function. It then returns a move closure that has taken word with it. greet can be called twice. Both calls print hello, learner!. make_counter returns an FnMut closure that owns its own count. Stored in a mut variable, it prints count = 1, then count = 2. make_message returns an FnOnce closure that hands its text out. So it can run only once. It prints message = one time only. In all three, move is what lets the closure outlive the function that made it. The Function Pointers lesson shows the Box<dyn Fn> fix.
Note
Returning move closures is how factory functions are built in Rust.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Why must a returned closure use move?