Guide

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)); // 6

Types 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)); // 15

Rust 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 f

Copy 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:

TraitThe closureCan call
Fnonly reads its capturesmany times
FnMutchanges its capturesmany times
FnOnceuses up its capturesonce

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 mut variable.
  • move takes ownership; use it for threads and returned closures.
  • Fn reads, FnMut changes, FnOnce uses up.