# Closures in Rust
I have recently become interested in closures and their usage in Rust, primarily because I have spent the past few weeks learning about and building web servers. When I first learned about closures and how to share objects between tasks (invoked functions, spawned threads), I found it helpful to think about these guides:
1. The lexical scope and the nesting of functions within an enclosing function.
1. Nested function(s) - named or anonymous - defined in the enclosing function.
1. Variables are scoped to the enclosing function, accessible to the nested function, and shared between the enclosing and enclosed functions.
1. The variables accessed by the closures are not copied by the closure. These variables are kept alive (on the heap) even after their enclosing function has been popped off the stack. Any updates to the variables will be reflected across the closures accessing them.
Some of these hold in Rust (somewhat), however, there are nuances to be considered, especially regarding ownership rules. In this article I try to outline what I have learned about closures in the past week, focusing on:
1. what closures are
1. variable access and capture
1. function and closure types
1. closures as function arguments
1. storing closures
### What are closures
Closures are anonymous function expressions. In the snippet below, an unnamed function is defined and assigned to the variable `mul`. This function is a closure and is characterized by the double pipe symbol `||` analogous to the brackets `()` of regular functions. Closures can take in arguments, as is shown, with specified parameters and return types. Although specifying these types is not necessary because Rust can infer what they are.
```rust=
fn main() {
let five = 5;
let ten = 10;
let mul = |a: i32, b: i32| -> i32 {
a * b
};
println!("{}", mul(five, ten));
}
```
In essence this is what a closure is. Within the `main` enclosing function, we define another anonymous function (assigned to `mul`), with variables `five` and `ten` that the closure has access to. These variables can be accessed by borrowing, copying, and moving. Depending on the variable type, variables can be dropped by closures. In the next section, I briefly explore variable access and capture.
### Variable Access and Capture
The process where a closure accesses the variables in the enclosing/parent scope is called capture. The rules of ownership apply and depending on the variable type, a closure can borrow a reference to, or move and claim ownership of the variable. This section briefly explores borrowing and moving.
#### Borrow
Closures are subject to the rules on lifetimes and borrowing. A closure that borrows a reference to an object should not outlive the object being referenced (i.e. the referent). "Everything borrowed must be returned eventually" (Blandy et al, 2021). In the example below, the closure enclosed borrows a reference to `name`. `enclosed` will never outlive `name`.
```rust
fn enclosing(name: String) -> String {
let enclosed = || format!("Hello {}", name);
enclosed()
}
fn main() {
let name = "Alice".to_string();
println!("{:#?}", enclosing(name));
}
```
```shell=
$ "Hello Alice"
```
#### Move
Closures do not always borrow a reference though. In the example below, the code sample has been extended to have a thread needle running parallel to the main thread. Because of the variable type, i.e. String for name, the code compiles successfully and outputs the result shown in the shell script below.
```rust
use std::thread;
fn enclosing(name: String) -> String {
let enclosed = || format!("Hello {}", name);
enclosed()
}
fn main() {
let name = "Alice".to_string();
let greeting = || enclosing(name);
let needle = thread::spawn(|| format!("Greeting: {:#?}", greeting()));
println!("{:#?}", needle.join());
}
```
```shell
Ok(
"Greeting: \"Hello Alice\"",
)
```
However, changing the variable type to &str, results in a curious error
```rust
use std::thread;
fn enclosing(name: &str) -> String {
let enclosed = || format!("Hello {}", name);
enclosed()
}
fn main() {
let name = "Alice";
let greeting = || enclosing(name);
let needle = thread::spawn(|| {
// Series of commands before `greeting`
format!("Greeting: {:#?}", greeting())
});
println!("{:#?}", needle.join());
}
```
```shell
closure may outlive the current function, but it borrows `greeting`, which is owned by the current function
```
The picture below shows how best I understand what's happening. Given that the spawned thread needle could be a long-running thread, the Rust has no guarantee that the variable name borrowed by greeting will outlive the thread. In this scenario, main could end after needle completes (i.e. end1) or before needle completes (i.e. end2). If it is the latter, main would drop name which it owns, while needle is making reference to an already destroyed variable.

To circumvent this, the closure must take ownership of, and not borrow a reference to name. It does so by using the keyword `move` as shown below. The code successfully compiles here.
```rust
fn main() {
let name = "Alice";
let greeting = || enclosing(name);
let needle = thread::spawn(move || {
// Series of commands before `greeting`
format!("Greeting: {:#?}", greeting())
});
println!("{:#?}", needle.join());
}
```
`&str` is a slice that must be passed by reference (Blandy et al, 2021, pg. 71, 75).
Closures follow a hierarchy to capture variables preferring to borrow a reference first, if possible, before moving.
Simple types that can be easily copied will not require the closure taking ownership. Types that are not `Copy` will be moved.
### Function and Closure Types
Closures, like functions, can be assigned to variables. They can also be passed as arguments to functions. This means that closures must have a type.
```rust
fn add(a: i32, b: i32) -> i32 {
a + b
}
```
The function add has a type `fn(i32, i32) -> i32`. Any function with this type can be passed as an argument as shown below with `addn`.
```rust
fn addn(a: i32, b: i32, op: fn(i32, i32) -> i32) -> i32 {
op(a, b)
}
```
Similarly, closures have their type. Closures are anonymous function expressions and are callable, but they are not functions. Closure expressions evaluate to unique anonymous struct-like types created by the compiler, i.e. "every closure you write has its own type" (Blandy et al, 2021). Given the uniqueness of closure types, the code that closures work with often tends to be generic.
The snippet below shows the generic function `generic_addition` that takes in any type that implements the `Fn` trait - a trait that all functions and most closures implement by default.
```rust
fn generic_addition<T>(a: i32, b: i32, op: T) -> i32
where T: Fn(i32, i32) -> i32
{
let num = op(a, b);
num
}
fn main() {
let five = 5;
let ten = 10;
let fifteen = generic_addition(five, ten, |five, ten| {
five + ten
});
println!("Fifteen: {}", fifteen);
}
```
Closure types are akin to structs containing references borrowed, values moved, or are empty structs when variables in the enclosing function are not accessed. For the closure passed to `generic_addition`, the "struct" representation is as shown below
```rust
struct Closure {
five: i32,
ten: i32,
}
```
Two other trait categories that closures implement are `FnOnce` and `FnMut`. The former allows the closure to be called just once and for captured variables to be dropped, while the latter allows closures with mutable captured data to be read and written to without the option of dropping them.
### Closures as function arguments
Previous examples have already shown how closures can be passed as argument to a generic function. However, if you are not dealing with generics, closures need to be boxed before they can be passed as function arguments. Boxing helps the function to support a variety of closure types and to know, at compile time, what their size (in the case, the size of an address) should be.
```rust
fn binary_op(a: u32, b: u32, func: Box<dyn Fn(u32, u32) -> u32>) -> u32 {
func(a, b)
}
fn main() {
// Multiply
let mul = binary_op(10, 5, Box::new(|a, b| a * b));
println!("Result: {}", mul);
// Add
let add = binary_op(10, 5, Box::new(|a, b| a + b));
println!("Result: {}", add);
// Subtract
let sub = binary_op(10, 5, Box::new(|a, b| a - b));
println!("Result: {}", sub);
}
```
In this manner, different closure types can be passed into `binary_op`.
### Storing Closures
This ability to place a closure on the heap with `Box` allows web frameworks to store different closure routes to their corresponding paths, and for one router to match incoming requests to these paths to their stored closure.
A contrived example of storing closures is as shown below
```rust
use std::collections::HashMap;
fn main() {
// Consider a hashmap of request path to route as displayed
// {
// "/": home,
// "/about": about,
// "/contact": contact,
// }
let mut route_map: HashMap<String, Box<dyn Fn(String) -> String>> = HashMap::new();
let home = |request: String| format!("Home page");
let about = |request: String| format!("About us page");
let contact = |request: String| format!("Contact us page");
route_map.insert("/home".to_string(), Box::new(home));
route_map.insert("/about".to_string(), Box::new(about));
route_map.insert("/contact".to_string(), Box::new(contact));
}
```
### Conclusion
Closures are anonymous functions. They are defined in an enclosing function and capture variables in their enclosing function's scope either by borrowing a reference or by moving them. They are not functions even though they are callable. Closures possess trait categories that allow them to be called just once, many times and many times if they hold a mutable reference to captured variables. Closures can be boxed and passed as arguments to functions. This very property finds extensive use in web frameworks and their implementation of a request router, which is quite common across different web frameworks like `iron` and `actix_web`.
### References
1. Closure Types https://doc.rust-lang.org/reference/types/closure.html
1. Blandy Jim, Jason Orendorff, & Tindall, F, S, Leonora (2021): Programming Rust
1. Jason Lennon (2022): Rust Programming: The Complete Developer's Guide. https://zerotomastery.io/courses/learn-rust/
1. Tim McNamara (2019): Rust in Action