Practical examples of examples of using closures in Rust
Quick, concrete examples of using closures in Rust
Let’s start with a few quick-hit examples of examples of using closures in Rust, then unpack why they work and where they shine.
fn main() {
// Inline filtering with a closure
let nums = vec![1, 2, 3, 4, 5, 6];
let evens: Vec<_> = nums
.into_iter()
.filter(|n| n % 2 == 0)
.collect();
// Capturing an external variable
let threshold = 10;
let bigger_than_threshold = |x: i32| x > threshold;
println!("evens: {:?}", evens);
println!("15 > threshold? {}", bigger_than_threshold(15));
}
These tiny snippets already hint at the real examples you’ll see across the Rust ecosystem: closures passed into iterator methods, closures capturing configuration, and closures used as callbacks.
Iterator-heavy examples of using closures in Rust
The most common example of closures in Rust is with iterators. If you’ve ever chained map, filter, or fold, you’ve already leaned on closures.
Mapping and transforming data
Rust’s iterator adapters are designed around closures. Here’s a realistic snippet that might appear in data processing or ETL-style code:
#[derive(Debug)]
struct User {
id: u64,
email: String,
active: bool,
}
fn active_user_emails(users: &[User]) -> Vec<String> {
users
.iter()
.filter(|u| u.active)
.map(|u| u.email.to_lowercase())
.collect()
}
In this example of closure usage, the filter and map calls each receive a closure. The closures capture nothing by value; they simply borrow u from the iterator. These are some of the best examples for beginners because they highlight how closures can stay lightweight and inline without cluttering the codebase with tiny named functions.
Aggregation with fold
Another classic example of examples of using closures in Rust is fold, which lets you accumulate a value as you iterate:
fn sum_of_squares(nums: &[i32]) -> i32 {
nums.iter().fold(0, |acc, n| acc + n * n)
}
The closure |acc, n| acc + n * n is passed directly to fold. It’s a pure function with no side effects, and because it’s a closure, it can capture environment if needed. In practice, this pattern appears in numeric code, log aggregation, and any place you’d otherwise write a loop with a running total.
Capturing environment: configuration and partial application
Real examples of closures in Rust almost always involve capturing some local configuration or state.
Closure as a configurable predicate
Imagine you’re writing a small filtering utility where the threshold is determined at runtime:
fn filter_above(nums: &[i32], threshold: i32) -> Vec<i32> {
let is_above = |x: &i32| *x > threshold; // captures `threshold` by borrow
nums.iter().copied().filter(is_above).collect()
}
Here the closure is_above closes over threshold. No global static, no extra struct; just a tiny, localized behavior customized by the surrounding scope. This pattern shows up in search filters, validation logic, and feature-flag checks.
Simulating partial application
Rust doesn’t have built-in currying, but closures give you a nice approximation:
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor
}
fn main() {
let times_two = make_multiplier(2);
let times_ten = make_multiplier(10);
println!("2 * 7 = {}", times_two(7));
println!("10 * 3 = {}", times_ten(3));
}
This is one of the best examples of using closures in Rust to build small domain-specific languages (DSL-style APIs). Libraries for configuration, pricing rules, or routing often expose functions that return closures like this.
Fn, FnMut, FnOnce: real examples from everyday Rust
You can’t talk about examples of examples of using closures in Rust without touching Fn, FnMut, and FnOnce. The names look academic, but the patterns are very down-to-earth.
Fn: read-only access
Closures that only read from captured variables implement Fn and can be called multiple times without mutation:
fn apply_twice<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(f(x))
}
fn main() {
let add_one = |n| n + 1;
println!("{}", apply_twice(add_one, 5)); // 7
}
This pattern shows up in retry logic, middleware stacks, and any API that needs a pure transformation.
FnMut: closures that mutate captured state
A more interesting example of using closures in Rust is when the closure needs to update something it captures:
fn call_with_logging<F>(mut f: F)
where
F: FnMut(i32) -> i32,
{
let inputs = [1, 2, 3];
for input in inputs {
let output = f(input);
println!("f({}) = {}", input, output);
}
}
fn main() {
let mut call_count = 0;
let mut tracked = |x: i32| {
call_count += 1; // mutation of captured variable
x * 2
};
call_with_logging(&mut tracked);
println!("call_count = {}", call_count);
}
This is a real example you might see in stateful iterators, metrics collection, or caching layers.
FnOnce: consuming captured values
Sometimes the closure consumes what it captures and can only be called once. That’s where FnOnce comes in.
fn consume_and_print<F>(f: F)
where
F: FnOnce(),
{
f();
}
fn main() {
let s = String::from("hello");
let consume_s = move || {
// `s` is moved into the closure
println!("{}", s);
};
consume_and_print(consume_s);
// can't use `consume_s` or `s` here
}
These examples include patterns you’ll see in thread::spawn, async executors, and one-shot callbacks where ownership must move into the closure.
Examples of using closures in Rust for callbacks and handlers
Closures make for ergonomic callbacks, especially in async and event-driven code.
Thread spawning and ownership
One widely taught example of using closures in Rust is std::thread::spawn, which takes a FnOnce closure by value:
use std::thread;
fn main() {
let data = vec![1, 2, 3, 4];
let handle = thread::spawn(move || {
let sum: i32 = data.iter().sum();
println!("sum in thread: {}", sum);
});
handle.join().unwrap();
}
The move keyword forces the closure to take ownership of data, matching the thread’s 'static lifetime requirement. This is not just a toy; it’s exactly how production Rust services pass work to background threads.
Simple async-style callback pattern
Even without a full async runtime, you can sketch a callback-based API with closures:
fn simulate_network_request<F>(url: &str, on_complete: F)
where
F: FnOnce(Result<String, String>),
{
// pretend we did some I/O
let response = format!("response from {}", url);
on_complete(Ok(response));
}
fn main() {
let url = "https://example.com";
simulate_network_request(url, |result| match result {
Ok(body) => println!("Success: {}", body),
Err(err) => eprintln!("Error: {}", err),
});
}
Modern async Rust often wraps this pattern in Futures, but the underlying idea—passing behavior as a closure—remains the same.
For deeper background on closures and ownership, the official Rust Book’s section on closures is still a gold standard reference: https://doc.rust-lang.org/book/ch13-01-closures.html.
Iterator pipelines: best examples of expressive Rust with closures
Some of the best examples of examples of using closures in Rust come from iterator pipelines that read almost like SQL or dataflow descriptions.
Combining filter, map, and group-like behavior
Consider log lines where you want to:
- Keep only
ERRORentries - Extract a code from each line
- Count frequency of each code
use std::collections::HashMap;
fn error_code_counts(lines: &[String]) -> HashMap<String, usize> {
let mut counts = HashMap::new();
lines
.iter()
.filter(|line| line.contains("ERROR"))
.filter_map(|line| {
line.split_whitespace()
.find(|part| part.starts_with("code="))
.map(|code_part| code_part.trim_start_matches("code=").to_string())
})
.for_each(|code| {
*counts.entry(code).or_insert(0) += 1;
});
counts
}
Every step here is an example of using closures in Rust to express intent right where it’s needed—no extra helper functions, no boilerplate.
Higher-order functions: closures as parameters and return values
Rust leans heavily on higher-order functions, especially in libraries that want to stay generic and flexible.
Passing closures into generic helpers
Here’s a pattern you’ll see in real-world code that needs pluggable behavior:
fn with_retry<F, T, E>(mut op: F, retries: u32) -> Result<T, E>
where
F: FnMut() -> Result<T, E>,
{
let mut attempts = 0;
loop {
attempts += 1;
match op() {
Ok(v) => return Ok(v),
Err(e) if attempts > retries => return Err(e),
Err(_) => continue,
}
}
}
fn main() {
let mut counter = 0;
let result = with_retry(
|| {
counter += 1;
if counter < 3 {
Err("not yet")
} else {
Ok("success")
}
},
5,
);
println!("result: {:?}", result);
}
This is a strong example of examples of using closures in Rust for infrastructure code: retries, backoff policies, and transactional operations.
Returning closures for flexible configuration
A library might expose a function that returns a preconfigured closure:
fn make_rate_limiter(max_per_minute: u32) -> impl FnMut() -> bool {
use std::time::{Duration, Instant};
let mut count = 0u32;
let mut window_start = Instant::now();
move || {
let now = Instant::now();
if now.duration_since(window_start) > Duration::from_secs(60) {
window_start = now;
count = 0;
}
if count < max_per_minute {
count += 1;
true
} else {
false
}
}
}
fn main() {
let mut limiter = make_rate_limiter(100);
assert!(limiter()); // allowed
}
This pattern shows up in API clients, job schedulers, and any system that wants to bundle policy with behavior.
2024–2025 context: how closures fit into modern Rust
In current Rust (1.80+ as of late 2024), closures continue to be the backbone of iterator-heavy and async-heavy code. Iterator adapters in std and crates like itertools lean on closures for expressive data transformation. Async ecosystems like tokio and async-std use closures in builder patterns, configuration hooks, and task spawning.
The Rust community’s best practices around closures are stable and well documented in resources like:
- The Rust Reference on closures: https://doc.rust-lang.org/reference/expressions/closure-expr.html
- The Rustonomicon’s discussion of advanced ownership and lifetimes: https://doc.rust-lang.org/nomicon/ (useful when closures meet unsafe or FFI)
While these aren’t health or medical sites, they play a similar role for Rust that organizations like the National Institutes of Health (NIH) or Harvard do for scientific and educational topics: long-lived, vetted, and widely trusted.
FAQ: short answers with concrete examples
What are some simple examples of using closures in Rust?
Simple examples include filtering a vector with filter(|x| x % 2 == 0), mapping values with map(|x| x * 2), and sorting with a custom comparator like sort_by(|a, b| a.len().cmp(&b.len())). These show closures passed directly into iterator and slice methods.
Can you give an example of a closure capturing a variable?
Yes. A classic example of a capturing closure is:
let threshold = 10;
let is_large = |x: i32| x > threshold;
Here is_large captures threshold by reference and can use it each time the closure is called.
Why use a closure instead of a normal function in Rust?
Use a closure when you want behavior that depends on local variables without threading them through every call. In the best examples, closures keep related logic close together—like a small predicate or transformation defined right next to the loop or iterator chain that uses it.
Are closures in Rust slow compared to functions?
In most real examples, no. Closures are usually monomorphized and inlined just like generic functions. When you pass them as generic parameters (F: Fn(...)), the compiler can optimize them aggressively. Dynamic trait objects like Box<dyn Fn()> do add indirection, but that’s a choice you make explicitly.
Do closures work with async Rust?
Yes. You’ll see closures used with async executors (for example, tokio::spawn(async move { ... })), in builder methods that configure timeouts or middleware, and in callback-style APIs that wrap async operations. The same ownership rules apply; move closures are common when you need to transfer data into an async task.
If you keep these real examples of examples of using closures in Rust in mind—iterators, callbacks, configuration, and higher-order helpers—you’ll start to see closures not as syntactic sugar, but as a very direct way to express behavior right where it matters.
Related Topics
Examples of Basic Syntax in Rust: 3 Practical Examples for Beginners
Best examples of defining functions in Rust: practical examples for 2025
Examples of Using Enums in Rust (With Practical Code Snippets)
Real-world examples of examples of testing in Rust
Practical examples of examples of using closures in Rust
Practical examples of examples of file I/O in Rust
Explore More Rust Code Snippets
Discover more examples and insights in this category.
View All Rust Code Snippets