CodeBlanks Logo Icon CodeBlanks

Fn, FnMut, FnOnce

Tell apart the three closure traits Fn, FnMut and FnOnce.

Fn, FnMut, FnOnce

Closures implement one (or several) of three traits depending on how they capture:

  • FnOnce — can be called at least once; may consume what it captured.
  • FnMut — can be called many times, may change what it captured.
  • Fn — can be called many times, only reads.
fn uygula<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
fn iki_kez<F: FnMut()>(mut f: F) { f(); f(); }
fn bir_kez<F: FnOnce() -> String>(f: F) -> String { f() }

Every Fn is also FnMut, and every FnMut is also FnOnce.

Choosing the right bound

The bound is chosen by what the function will do with the closure:

let mut n = 0;
iki_kez(|| n += 3);                // FnMut: changes n
println!("{n}");                   // 6

let s = String::from("son");
println!("{}", bir_kez(move || s));   // FnOnce: returns and consumes s
println!("{}", uygula(|x| x + 10, 5)); // Fn: 15

move || s can't be passed to iki_kez; s would no longer exist on the second call.

Practice Three traits

Write the functions uygula, iki_kez and bir_kez above. Print the results of uygula(|x| x + 10, 5), n after adding 3 twice, and bir_kez(move || s) (s = "son") separated by spaces.

Starter code

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
fn uygula<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
    f(x)
}

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

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

fn main() {
    let a = uygula(|x| x + 10, 5);
    let mut n = 0;
    iki_kez(|| n += 3);
    let s = String::from("son");
    let c = bir_kez(move || s);
    println!("{} {} {}", a, n, c);
}

Expected output

15 6 son

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 When taking a closure as a parameter, which bound is the most flexible (accepts the most closures)?

  • FnOnce Correct answer
  • Fn
  • FnMut
  • All equal

Explanation: Every closure is FnOnce. But if the function will call the closure more than once, it must ask for FnMut or Fn.

2 Bound for a mutating closure

Question
fn iki_kez<F: ____()>(mut f: F) {
    f();
    f();
}
Answer
fn iki_kez<F: FnMut()>(mut f: F) {
    f();
    f();
}

Explanation: A closure called several times that may change state needs FnMut.

3 Closure and the narrowest trait it implements

Term Meaning
|x| x + 10 Fn
|| n += 3 FnMut
move || s (returns a String) FnOnce
|| println!("{x}") Fn (reads)

All lessons in Intermediate