Closures
A closure is a small function with no name that you can store and pass around.
Closure syntax
Put the inputs between bars, then the body.
let add_one = |x: i32| x + 1;let both = |a: i32, b: i32| { let s = a + b; s * 2};println!("{}", add_one(4)); // 5println!("{}", both(1, 2)); // 6Types are often left out, and Rust works them out from the first call. After that, the types are fixed.
Capturing variables
A closure can use variables from the place where it was made. A normal fn can't do this.
let n = 10;let add_n = |x| x + n; // reads nprintln!("{}", add_n(5)); // 15Rust picks the lightest way to capture:
- read only: a shared borrow
- change it: a mutable borrow (the closure variable must be
mut) - use it up: take ownership
move closures
Write move before the bars to make the closure take ownership of what it uses. Use it when the closure may outlive those variables, like in a thread.
let v = vec![1, 2];let f = move || v.len();println!("{}", f());// v can't be used here, it moved into fCopy types like i32 are copied in, so the old variable still works.
A function can return a closure as impl Fn() -> String (inputs in the brackets, output after ->). Add move so the closure owns what it uses.
Fn, FnMut, FnOnce
Every closure has one or more of these traits:
| Trait | The closure | Can call |
|---|---|---|
Fn | only reads its captures | many times |
FnMut | changes its captures | many times |
FnOnce | uses up its captures | once |
Every closure is at least FnOnce. A function that takes F: Fn() will not accept a closure that changes a capture.
Remember
- A closure that changes a capture must be stored in a
mutvariable. movetakes ownership; use it for threads and returned closures.Fnreads,FnMutchanges,FnOnceuses up.