Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

Rust Closures

A closure is a small function without a name that you can write right where you need it, store in a variable, or hand to another function. If a normal function is a recipe written in a cookbook, a closure is a quick note on a sticky label: short, handy, and used on the spot.

Closures use vertical bars | | for their parameters, followed by the body:

fn main() {
    let double = |x: i32| x * 2;
    let greet = |name: &str| format!("Hello, {}!", name);
    println!("{}", double(21));
    println!("{}", greet("Neha"));
}
Output
42
Hello, Neha!

For a closure with a longer body, use curly brackets, just like a function: |x| { let y = x + 1; y * y }. Rust can usually work out the types of the parameters, so you often leave them out.

Closures remember their surroundings

Here is the special thing about closures: they can use variables from the place where they were created. This is called capturing the environment. A normal fn cannot do this.

fn main() {
    let discount = 15;
    let final_price = |price: u32| price - price * discount / 100;
    println!("400 becomes {}", final_price(400));
    println!("80 becomes {}", final_price(80));
}
Output
400 becomes 340
80 becomes 68

The closure "remembers" discount. If you need a closure to change a captured variable, the variable must be mut, and the closure itself must be stored in a mut variable too:

fn main() {
    let mut clicks = 0;
    let mut click = || clicks += 1;
    click();
    click();
    click();
    println!("Clicked {} times", clicks);
}
Output
Clicked 3 times

Passing closures to methods

The most common use of closures is to tell a method how to do its job. Sorting by a key, keeping some items, finding the first match: each takes a closure.

fn main() {
    let mut words = vec!["banana", "fig", "cherry", "kiwi"];
    words.sort_by_key(|w| w.len());
    println!("Shortest first: {:?}", words);

    let long: Vec<_> = words.iter().filter(|w| w.len() > 4).collect();
    println!("Long words: {:?}", long);

    let starts_with_c = words.iter().find(|w| w.starts_with('c'));
    println!("First with c: {:?}", starts_with_c);
}
Output
Shortest first: ["fig", "kiwi", "banana", "cherry"]
Long words: ["banana", "cherry"]
First with c: Some("cherry")

sort_by_key keeps items with the same key in their original order, which is why "banana" stays before "cherry".

Functions that take closures

You can write your own functions that accept a closure. The type impl Fn(i32) -> i32 means "anything that can be called with an i32 and gives back an i32":

fn apply_twice(f: impl Fn(i32) -> i32, value: i32) -> i32 {
    f(f(value))
}

fn main() {
    println!("{}", apply_twice(|x| x + 10, 1));
    println!("{}", apply_twice(|x| x * x, 3));
}
Output
21
81

Practice exercises

  1. Write a closure is_even and use it to print which numbers from 1 to 10 are even.
  2. Sort a vector of names by their last letter using sort_by_key.
  3. Write fn repeat(times: u32, f: impl Fn(u32)) that calls f with 1, 2, 3 and so on.