| |

Rust 51 🦀 Closure Traits: Fn, FnMut, and FnOnce

⏱️ Estimated read: 20 min | |

Every closure in Rust implements one or more of three traits: Fn, FnMut, and FnOnce. These traits describe how the closure uses the values it captures from its environment. A closure that only reads its captures implements Fn. A closure that mutates its captures implements FnMut. A closure that consumes its captures implements FnOnce. The compiler infers which traits apply based on the closure body, and every function that accepts a closure must decide which trait bound to require. Getting this right is what allows closures to be passed to Iterator::map, Option::map, thread::spawn, and every other API that takes a callback.

Key point: The three traits form a hierarchy. Every Fn is also FnMut, and every FnMut is also FnOnce. A function that requires FnOnce accepts all closures. A function that requires Fn accepts only closures that do not mutate or consume their captures. The tighter the bound, the more the function can do with the closure but the fewer closures it accepts.


Why the three traits exist

The ownership problem. A closure captures variables from its environment. Rust’s ownership rules apply to those captures just as they apply to any other value. A closure that reads a captured variable needs a shared borrow. A closure that mutates a captured variable needs a mutable borrow. A closure that moves a captured variable out needs ownership. The three traits encode these three access patterns.

The call-site problem. When a function calls a closure, the call may happen once, many times, or the closure may be moved into another thread and called there. The function must declare how it intends to use the closure so that callers know what the closure is allowed to do. FnOnce says the closure will be called at most once. FnMut says it may be called many times and may mutate state. Fn says it may be called many times and only reads state.

The polymorphism problem. A function that takes a closure should accept as many closures as possible. The trait bounds determine which closures are accepted. Choosing the loosest bound that still allows the function’s implementation to compile maximizes the set of accepted closures.

The inference problem. The compiler infers the traits from the closure body. A closure that only reads a variable infers Fn. A closure that calls push on a captured Vec infers FnMut. A closure that moves a captured String into its body infers FnOnce. The inference is automatic, and the developer sees it only when a function’s bounds reject a closure that the compiler inferred as a stricter trait.

The API design problem. Functions like Iterator::map require FnMut because they call the closure once per element and do not need to consume it. thread::spawn requires FnOnce because the closure is moved into the new thread and called exactly once. Option::unwrap_or_else requires FnOnce because the closure is only called in the None case. Each API’s bound reflects how it uses the closure.

The move keyword problem. A closure can be prefixed with move, which forces all captures to be taken by value. A move closure can still implement Fn or FnMut if it does not consume its captures, but the captures are owned by the closure regardless. The interaction between move and the three traits is a common source of confusion.


a. The three traits

The three traits are defined in the standard library. Each has a call method that takes the closure’s arguments and returns the closure’s output.

FnOnce

FnOnce is the most permissive trait from the caller’s perspective. It says the closure can be called at least once, and possibly only once. A closure that moves a captured value out of its body implements FnOnce and not the other two.

fn call_once<F: FnOnce() -> String>(f: F) -> String {
    f()
}

fn main() {
    let s = String::from("hello");
    let closure = move || s;  // Moves s out of the closure.
    let result = call_once(closure);
    println!("{}", result);  // "hello"
}

The closure consumes s when called. It cannot be called again because s is gone. The bound FnOnce accepts it.

FnMut

FnMut says the closure can be called multiple times and may mutate its captures. A closure that pushes to a captured Vec implements FnMut. It does not implement Fn because Fn requires that the closure not mutate its captures.

fn call_mut<F: FnMut()>(mut f: F) {
    f();
    f();
}

fn main() {
    let mut count = 0;
    let closure = || count += 1;  // Mutates count.
    call_mut(closure);
    println!("{}", count);  // 2
}

The mut on the parameter is required because calling an FnMut closure mutates the closure itself, since the closure holds the mutable borrow of count.

Fn

Fn says the closure can be called multiple times and only reads its captures. A closure that prints a captured variable implements Fn.

fn call_fn<F: Fn() -> i32>(f: F) -> i32 {
    f() + f()
}

fn main() {
    let x = 10;
    let closure = || x + 1;  // Only reads x.
    let result = call_fn(closure);
    println!("{}", result);  // 22
}

The closure reads x by shared borrow. It can be called any number of times.

The hierarchy

Every Fn is FnMut, and every FnMut is FnOnce. This is declared in the standard library:

impl<A, F: Fn<A> + ?Sized> FnMut<A> for F {}
impl<A, F: FnMut<A> + ?Sized> FnOnce<A> for F {}

The implication means that a function requiring FnOnce accepts closures of all three kinds. A function requiring FnMut accepts FnMut and Fn. A function requiring Fn accepts only Fn.

Fn ⊂ FnMut ⊂ FnOnce

The tighter the bound, the smaller the set of accepted closures, but the more the function can do with the closure.


b. Inference and the move keyword

The compiler infers which traits a closure implements. The inference is based on how the closure body uses the captured variables, and the move keyword changes the capture mode but not the trait.

Inference from usage

A closure that reads a variable infers Fn. A closure that mutates a variable infers FnMut. A closure that moves a variable out infers FnOnce.

let x = 5;
let f = || println!("{}", x);  // Fn

let mut y = 5;
let g = || y += 1;  // FnMut

let s = String::from("hello");
let h = || drop(s);  // FnOnce

The compiler analyzes the closure body and determines the least restrictive trait that the closure can implement. It never infers a stricter trait than necessary.

The move keyword

The move keyword forces all captures to be taken by value. Without move, the closure captures by reference when possible. With move, the closure owns its captures.

let s = String::from("hello");
let f = move || println!("{}", s);  // Owns s, but only reads it.

The closure f owns s, but it still implements Fn because it does not mutate or consume s. Ownership and trait are separate concerns.

let mut count = 0;
let f = move || count += 1;  // Owns count, mutates it.
// Implements FnMut, not Fn.

The closure owns count and mutates it. It implements FnMut. The move keyword does not make it FnOnce; only consuming a capture does.

let s = String::from("hello");
let f = move || drop(s);  // Owns s, consumes it.
// Implements FnOnce.

The closure owns s and drops it. It can only be called once.

The interaction with borrows

A move closure takes ownership of its captures, which means the original variables are no longer usable in the enclosing scope. This is the price of moving the closure into a thread or storing it beyond the enclosing scope’s lifetime.

let s = String::from("hello");
let f = move || println!("{}", s);
// println!("{}", s);  // Error: s was moved into f.

The compiler rejects any use of s after the move closure captures it.

The inferred trait with move

The move keyword does not change which trait the closure implements. The trait is determined by what the closure body does with its captures, regardless of how the captures were obtained.

// move closure implementing Fn
let s = String::from("hello");
let f = move || println!("{}", s);

// move closure implementing FnMut
let mut s = String::from("hello");
let g = move || s.push('!');

// move closure implementing FnOnce
let s = String::from("hello");
let h = move || drop(s);

The distinction matters when a function’s bound is Fn and the developer has written a move closure that only reads its captures. The closure implements Fn, so the bound is satisfied.

The capture modes

Without move, a closure captures by the least restrictive mode it can. If the body only reads a variable, the closure captures by shared reference. If it mutates, it captures by mutable reference. If it moves, it captures by value.

let x = 5;
let f = || println!("{}", x);  // Captures x by &x.

let mut y = 5;
let g = || y += 1;  // Captures y by &mut y.

let s = String::from("hello");
let h = || drop(s);  // Captures s by value.

The compiler infers the capture mode from the body, and the trait follows from the capture mode. move forces all captures to by-value, but the trait still follows from the body.


c. Choosing trait bounds

The function’s trait bound determines which closures it accepts. The rule is to choose the loosest bound that still allows the function’s implementation to compile.

The loosest bound that works

A function that calls the closure once and does not need to keep it can require FnOnce. This accepts every closure.

fn call_once<F: FnOnce() -> i32>(f: F) -> i32 {
    f()
}

The implementation calls f exactly once. FnOnce is sufficient, so the function accepts all three closure kinds.

The FnMut bound for repeated calls with mutation

A function that calls the closure multiple times and allows mutation requires FnMut.

fn call_twice<F: FnMut()>(mut f: F) {
    f();
    f();
}

The implementation calls f twice. FnMut allows the closure to mutate state between calls. A closure that implements Fn also works, because Fn implies FnMut.

The Fn bound for repeated calls without mutation

A function that calls the closure multiple times and does not allow mutation requires Fn. This is the strictest bound.

fn call_thrice<F: Fn() -> i32>(f: F) -> i32 {
    f() + f() + f()
}

A closure that mutates its captures does not implement Fn, so the function rejects it. This is appropriate when the function needs to call the closure concurrently or interleave calls with reads of the same data.

The fn pointer bound

A fn pointer is a function defined with fn, not a closure. A function that accepts only fn pointers uses the fn type directly, without a trait bound. fn pointers implement all three closure traits.

fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
    f(x)
}

The fn bound rejects closures that capture variables, because capturing closures are not fn pointers. This is useful when the function needs to store the callback in a struct with a fixed-size representation.

The generic versus impl Trait

A generic parameter with a trait bound and an impl Trait parameter are equivalent in most cases. The generic form allows explicit type annotation and turbofish syntax. The impl Trait form is shorter and often clearer.

fn apply_generic<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
    f(x)
}

fn apply_impl(f: impl Fn(i32) -> i32, x: i32) -> i32 {
    f(x)
}

The two are functionally identical for function arguments. The generic form is necessary when the caller needs to specify the type explicitly, or when the function has multiple trait bounds.

The trait object bound

A function that accepts a boxed closure uses a trait object.

fn apply_boxed(f: Box<dyn Fn(i32) -> i32>, x: i32) -> i32 {
    f(x)
}

The trait object erases the concrete closure type, which allows the function to accept closures of different types at different call sites or to store them in collections. The cost is dynamic dispatch and an allocation.

The hierarchy at the bound

Because Fn implies FnMut and FnMut implies FnOnce, the bound determines the accepted set. A function bounded by FnOnce accepts FnOnce, FnMut, and Fn closures. A function bounded by Fn accepts only Fn closures.

// Accepts all closures.
fn a<F: FnOnce()>(f: F) {}

// Accepts FnMut and Fn closures.
fn b<F: FnMut()>(mut f: F) {}

// Accepts only Fn closures.
fn c<F: Fn()>(f: F) {}

The loosest bound maximizes the accepted set. The tightest bound maximizes what the function can do with the closure.


Complete Example Session

// ============================================
// PART 1: FnOnce — MOVES OUT OF CAPTURES
// ============================================

fn take_once<F: FnOnce() -> String>(f: F) -> String {
    f()
}

fn main() {
    let s = String::from("hello");
    let closure = move || s;
    let result = take_once(closure);
    println!("{}", result);  // hello
}

// ============================================
// PART 2: FnMut — MUTATES CAPTURES
// ============================================

fn call_mut<F: FnMut()>(mut f: F) {
    f();
    f();
    f();
}

fn main() {
    let mut count = 0;
    call_mut(|| count += 1);
    println!("{}", count);  // 3
}

// ============================================
// PART 3: Fn — READS CAPTURES
// ============================================

fn call_fn<F: Fn() -> i32>(f: F) -> i32 {
    f() + f()
}

fn main() {
    let x = 10;
    let result = call_fn(|| x + 1);
    println!("{}", result);  // 22
}

// ============================================
// PART 4: THE HIERARCHY
// ============================================

fn accepts_once<F: FnOnce()>(f: F) {}
fn accepts_mut<F: FnMut()>(mut f: F) {}
fn accepts_fn<F: Fn()>(f: F) {}

fn main() {
    let x = 5;

    // A Fn closure works everywhere.
    accepts_once(|| println!("{}", x));
    accepts_mut(|| println!("{}", x));
    accepts_fn(|| println!("{}", x));

    // A FnMut closure does not work as Fn.
    let mut y = 5;
    // accepts_fn(|| y += 1);  // Error: not Fn.
    accepts_mut(|| y += 1);
    accepts_once(|| y += 1);
}

// ============================================
// PART 5: INFERENCE FROM USAGE
// ============================================

fn main() {
    let x = 5;
    let f = || x;  // Infers Fn.

    let mut y = 5;
    let g = || y += 1;  // Infers FnMut.

    let s = String::from("hi");
    let h = || drop(s);  // Infers FnOnce.
}

// ============================================
// PART 6: THE MOVE KEYWORD
// ============================================

fn main() {
    let s = String::from("hello");
    let f = move || println!("{}", s);  // Owns s, but is Fn.

    let mut count = 0;
    let g = move || count += 1;  // Owns count, is FnMut.

    let s2 = String::from("bye");
    let h = move || drop(s2);  // Owns s2, is FnOnce.
}

// ============================================
// PART 7: THE LOOSEST BOUND
// ============================================

fn apply<F: FnOnce(i32) -> i32>(f: F, x: i32) -> i32 {
    f(x)
}

fn main() {
    // All three closure kinds are accepted.
    println!("{}", apply(|x| x + 1, 10));  // Fn

    let mut counter = 0;
    println!("{}", apply(|x| { counter += 1; x }, 10));  // FnMut

    let s = String::from("hi");
    println!("{}", apply(move |x| { drop(s); x }, 10));  // FnOnce
}

// ============================================
// PART 8: THE Fn BOUND
// ============================================

fn call_twice<F: Fn() -> i32>(f: F) -> i32 {
    f() + f()
}

fn main() {
    let x = 5;
    println!("{}", call_twice(|| x * 2));  // 20

    // A mutating closure does not compile.
    // let mut y = 0;
    // call_twice(|| { y += 1; y });  // Error: not Fn
}

// ============================================
// PART 9: FN POINTERS
// ============================================

fn add_one(x: i32) -> i32 {
    x + 1
}

fn apply_ptr(f: fn(i32) -> i32, x: i32) -> i32 {
    f(x)
}

fn main() {
    println!("{}", apply_ptr(add_one, 10));  // 11

    // A capturing closure is not a fn pointer.
    // let y = 5;
    // apply_ptr(|x| x + y, 10);  // Error
}

// ============================================
// PART 10: TRAIT OBJECTS
// ============================================

fn apply_boxed(f: Box<dyn Fn(i32) -> i32>, x: i32) -> i32 {
    f(x)
}

fn main() {
    let closures: Vec<Box<dyn Fn(i32) -> i32>> = vec![
        Box::new(|x| x + 1),
        Box::new(|x| x * 2),
    ];
    for f in closures {
        println!("{}", apply_boxed(f, 10));
    }
}

// A trait object erases the concrete type.
// The cost is dynamic dispatch and heap allocation.
// Useful when multiple closure types must coexist in a collection.

The ten parts progressed from each trait individually through the hierarchy, inference, the move keyword, bound selection, fn pointers, and trait objects. The same closure examples ran through every part, showing how the traits and bounds interact.


Quick Reference

The Three Traits

TraitMethodCapturesCall Count
FnOncecall_onceConsumesAt least once
FnMutcall_mutMutable borrowMany times
FncallShared borrowMany times

The Hierarchy

ImplicationAccepted By
Fn: FnMutFnMut bound accepts Fn closures
FnMut: FnOnceFnOnce bound accepts all closures
Fn: FnOnceTransitive

Bound Selection

Function DoesBoundAccepts
Calls once, consumesFnOnceAll closures
Calls many times, mutatesFnMutFnMut, Fn
Calls many times, no mutationFnFn only
Stores as fn pointerfnNon-capturing only
Stores as trait objectBox<dyn Fn>All via allocation

Inference Rules

Closure BodyInferred Trait
Reads captureFn
Mutates captureFnMut
Moves capture outFnOnce
Drops captureFnOnce
Calls another closureDepends on inner

Best Practices

✅ Do This:

// Use the loosest bound that works.
fn run<F: FnOnce()>(f: F) { f(); }  // ✅

// Use FnMut when mutation is needed across calls.
fn each<F: FnMut(i32)>(mut f: F) { for i in 0..3 { f(i); } }  // ✅

// Use Fn when concurrent or interleaved calls are needed.
fn sum<F: Fn(i32) -> i32>(f: F, xs: &[i32]) -> i32 { ... }  // ✅

// Use move to transfer ownership into the closure.
let f = move || println!("{}", s);  // ✅

// Use fn pointers when captures are not needed.
fn apply(f: fn(i32) -> i32, x: i32) -> i32 { f(x) }  // ✅

// Use Box<dyn Fn> when closures of different types must coexist.
let fs: Vec<Box<dyn Fn(i32) -> i32>> = vec![...];  // ✅

❌ Don’t Do This:

// Don't require Fn when FnOnce suffices.
fn run<F: Fn()>(f: F) { f(); }  // ❌ Rejects mutating closures

// Don't call an FnMut closure more than once without mut.
fn bad<F: FnMut()>(f: F) { f(); f(); }  // ❌ f must be mut

// Don't try to call an FnOnce closure twice.
fn bad<F: FnOnce()>(f: F) { f(); f(); }  // ❌ Use of moved value

// Don't assume move makes a closure FnOnce.
let f = move || println!("{}", x);  // ❌ Still Fn if only reads

// Don't use fn pointers for capturing closures.
fn apply(f: fn() -> i32) {}
let y = 5;
// apply(|| y);  // ❌ Capturing closure is not a fn pointer

// Don't use Box<dyn Fn> when a generic suffices.
fn run(f: Box<dyn Fn()>) {}  // ❌ Allocation cost

Common Pitfalls

PitfallWhy It HappensFix
expected Fn, found FnMutFunction bound too strictUse FnMut or FnOnce
cannot borrow as mutableFnMut parameter not mutAdd mut to the parameter
use of moved valueFnOnce called twiceCall only once
Captured value unavailablemove closure took ownershipDon’t use after the closure
Not a fn pointerClosure captures variablesRemove captures or use a trait
Cannot infer traitAmbiguous usageAdd explicit type annotation
Trait object not SizedBox<dyn Fn> requiredBox the closure
Closure too genericMultiple bounds conflictChoose the loosest bound

Real-World Examples

1. FnOnce with thread::spawn

let handle = std::thread::spawn(move || {
    println!("in thread");
});

2. FnMut with Iterator::for_each

let mut sum = 0;
(1..=5).for_each(|x| sum += x);

3. Fn with Iterator::map

let doubled: Vec<i32> = (1..=5).map(|x| x * 2).collect();

4. move with Thread

let s = String::from("hello");
let handle = std::thread::spawn(move || s.len());

5. FnOnce with Option::unwrap_or_else

let value = None.unwrap_or_else(|| expensive_computation());

6. FnMut with a Stateful Callback

fn run<F: FnMut(i32)>(mut f: F) {
    for i in 0..3 { f(i); }
}

7. Fn with Multiple Calls

fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
    f(f(x))
}

8. fn Pointer

fn double(x: i32) -> i32 { x * 2 }
let f: fn(i32) -> i32 = double;

9. Box

let handlers: Vec<Box<dyn Fn()>> = vec![
    Box::new(|| println!("a")),
    Box::new(|| println!("b")),
];

10. Returning a Closure

fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x + n
}

Visual

The Trait Hierarchy

┌────────────────────────────────────────────────────────────┐
│  CLOSURE TRAIT HIERARCHY                                   │
│                                                            │
│  ┌──────────────────────────────────────────────────┐     │
│  │  FnOnce                                           │     │
│  │    Can be called at least once.                   │     │
│  │    May consume its captures.                      │     │
│  │                                                   │     │
│  │    ┌──────────────────────────────────────┐       │     │
│  │    │  FnMut                                │       │     │
│  │    │    Called many times.                 │       │     │
│  │    │    May mutate captures.               │       │     │
│  │    │                                       │       │     │
│  │    │    ┌────────────────────────────┐     │       │     │
│  │    │    │  Fn                        │     │       │     │
│  │    │    │    Called many times.      │     │       │     │
│  │    │    │    Only reads captures.    │     │       │     │
│  │    │    └────────────────────────────┘     │       │     │
│  │    └──────────────────────────────────────┘       │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  Fn ⊂ FnMut ⊂ FnOnce                                       │
│  A bound of FnOnce accepts all three.                      │
│  A bound of Fn accepts only Fn.                            │
│                                                            │
└────────────────────────────────────────────────────────────┘

Inference from Usage

┌────────────────────────────────────────────────────────────┐
│  HOW THE COMPILER INFERS THE TRAIT                         │
│                                                            │
│  Closure body:                                             │
│  ┌──────────────────────────────────────────────────┐     │
│  │  || x                       Reads x → Fn          │     │
│  │  || x += 1                  Mutates x → FnMut     │     │
│  │  || drop(x)                 Consumes x → FnOnce   │     │
│  │  || println!("{}", x)       Reads x → Fn          │     │
│  │  || v.push(x)               Mutates v → FnMut     │     │
│  │  || s.into_bytes()          Consumes s → FnOnce   │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  The compiler picks the least restrictive trait            │
│  that the body allows.                                     │
│                                                            │
└────────────────────────────────────────────────────────────┘

The move Keyword

┌────────────────────────────────────────────────────────────┐
│  move AND THE THREE TRAITS                                 │
│                                                            │
│  move || println!("{}", s)                                 │
│    Owns s. Only reads. Implements Fn.                      │
│                                                            │
│  move || s.push('!')                                       │
│    Owns s. Mutates. Implements FnMut.                      │
│                                                            │
│  move || drop(s)                                           │
│    Owns s. Consumes. Implements FnOnce.                    │
│                                                            │
│  move changes HOW captures are taken.                      │
│  It does not change WHICH trait the closure implements.    │
│  The body determines the trait.                            │
│                                                            │
└────────────────────────────────────────────────────────────┘

Bound Selection

┌────────────────────────────────────────────────────────────┐
│  CHOOSING THE BOUND                                        │
│                                                            │
│  What does the function do with the closure?               │
│                                                            │
│  Calls it once, then drops it:                             │
│    → FnOnce bound. Accepts all closures.                   │
│                                                            │
│  Calls it many times, allows mutation:                     │
│    → FnMut bound. Accepts FnMut and Fn.                    │
│    Requires mut on the parameter.                          │
│                                                            │
│  Calls it many times, no mutation allowed:                 │
│    → Fn bound. Accepts only Fn.                            │
│                                                            │
│  Stores it as a plain function pointer:                    │
│    → fn type. Accepts only non-capturing functions.        │
│                                                            │
│  Stores it as a trait object:                              │
│    → Box<dyn Fn>. Accepts all, with allocation.            │
│                                                            │
│  Rule: use the loosest bound that compiles.                │
│                                                            │
└────────────────────────────────────────────────────────────┘

Summary

ItemValue
FnOnceCalled at least once; may consume captures
FnMutCalled many times; may mutate captures
FnCalled many times; only reads captures
HierarchyFn ⊂ FnMut ⊂ FnOnce
InferenceBased on closure body usage
moveForces by-value capture; does not change trait
FnOnce boundAccepts all closures
FnMut boundAccepts FnMut and Fn
Fn boundAccepts only Fn
fn pointerAccepts only non-capturing functions
Box<dyn Fn>Accepts all, with allocation and dynamic dispatch

Key takeaways:

  • The three traits describe how a closure uses its captures. Fn reads, FnMut mutates, FnOnce consumes. The compiler infers the trait from the closure body, and the trait determines which functions accept the closure.
  • The traits form a hierarchy. Fn implies FnMut implies FnOnce. A function bounded by FnOnce accepts every closure; a function bounded by Fn accepts only closures that do not mutate or consume.
  • The loosest bound maximizes accepted closures. A function should require FnOnce unless its implementation needs to call the closure more than once. FnMut is required only when the closure must be called repeatedly with the possibility of mutation. Fn is required only when the closure must be callable without mutation, such as when it is shared across threads.
  • The move keyword does not change the trait. It forces by-value capture, but the trait is still determined by the closure body. A move closure that only reads its captures is Fn.
  • The mut keyword on the parameter is required for FnMut. Calling an FnMut closure mutates the closure itself, so the parameter must be declared mutable.
  • fn pointers are a separate type. They accept only non-capturing functions. Capturing closures cannot be stored in fn pointers, but they can be stored in Box<dyn Fn>.
  • Trait objects erase the closure type. Box<dyn Fn> allows closures of different types to coexist in a collection, at the cost of heap allocation and dynamic dispatch.
  • The bound should match the function’s intent. A function that calls the closure once uses FnOnce. A function that calls it repeatedly and allows mutation uses FnMut. A function that calls it repeatedly and must not allow mutation uses Fn. The bound is a contract, and the compiler enforces it.

Remember: Every closure in Rust implements one or more of Fn, FnMut, and FnOnce. The traits describe how the closure uses its captures: Fn reads, FnMut mutates, FnOnce consumes. The traits form a hierarchy, so a bound of FnOnce accepts every closure while a bound of Fn accepts only closures that read. The compiler infers the trait from the closure body, and the move keyword changes how captures are taken without changing which trait the closure implements. Choosing the right bound is a matter of choosing the loosest one that allows the function to compile: FnOnce when the closure is called once, FnMut when it is called repeatedly with mutation, Fn when it is called repeatedly without mutation. The bound is the contract between the function and its caller, and the compiler enforces it.


Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!