What Are Closures?
A closure, also known as an anonymous function or lambda expression, is a function defined within the context of its usage. They allow functions to be defined within the scope of another function and can capture variables from their surrounding scope.
In Rust, closures are defined using a pair of vertical bars (||), which act like the brackets of a function. Between these bars, we specify the arguments to the function, followed by the code block that constitutes the body of the function. Let's look at a basic example:
let add_one = |x| x + 1;
println!("{}", add_one(5)); // Output: 6
Here, add_one is a closure that takes one argument, x, and returns x + 1.
Capturing Variables
A strong feature of closures is their ability to capture variables from their surrounding environment, commonly called upvar capturing. Variables can be captured in three ways:
- By reference:
&T - By mutable reference:
&mut T - By value:
T
Rust will try to infer the least restrictive choice based on how you use the variables within the closure. Let's demonstrate this:
let x = 7;
let borrow = || println!("borrow: {}", x);
let borrow_mut = || println!("borrow_mut: {}", x);
let move = move || println!("move: {}", x);
borrow();
borrow_mut();
move();
In this example, borrow captures x by reference (&T), borrow_mut captures x by mutable reference (&mut T), and move captures x by value (T). The move keyword is used to force the closure to take ownership of the values it's using.
Type Inference and Genericity
Rust's closure implementation shines when it comes to type inference and genericity. While function definitions require explicit types for their arguments, Rust closures do not. Additionally, unlike functions, closures can be generic over their input types. Here's an example:
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
let n = example_closure(5);
In this code, example_closure can take either a String or an i32 because it is generic.
Fn, FnMut, and FnOnce
In Rust, we have three traits to represent three kinds of closures: Fn, FnMut, and FnOnce, which correspond to the three ways of capturing variables:
Fntakes variables by reference (&T)FnMuttakes variables by mutable reference (&mut T)FnOncetakes variables by value (T)


