Fn, FnMut, FnOnce
Tutorial Video
Text description (accessibility)
This video demonstrates the "Fn, FnMut, FnOnce" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Higher-order functions need to express constraints on how many times and in what way a callable can be invoked. Key difference from OCaml: 1. **Type
Tutorial
The Problem
Higher-order functions need to express constraints on how many times and in what way a callable can be invoked. A sort key function must be callable many times without side effects — Fn. A stateful counter must be called multiple times with mutation — FnMut. A function that consumes its captured data should only be called once — FnOnce. Without these distinctions, the type system could not prevent: calling a one-shot closure twice (use-after-move), forgetting to declare mutable access, or passing a mutable closure where immutable sharing is expected.
🎯 Learning Outcomes
F: Fn(), F: FnMut(), F: FnOnce()Fn: FnMut: FnOnce (subtype relationship)mut f for FnMut callersdyn Fn/dyn FnMut/dyn FnOnce for trait objectsCode Example
#![allow(clippy::all)]
//! # Fn FnMut FnOnce — Closure Traits
/// Fn: can be called multiple times, doesn't mutate state
pub fn call_fn<F: Fn() -> i32>(f: F) -> i32 {
f() + f()
}
/// FnMut: can be called multiple times, may mutate state
pub fn call_fn_mut<F: FnMut() -> i32>(mut f: F) -> i32 {
f() + f()
}
/// FnOnce: can only be called once, consumes captured values
pub fn call_fn_once<F: FnOnce() -> String>(f: F) -> String {
f()
}
pub fn example_fn() -> i32 {
let x = 10;
call_fn(|| x) // Borrows x immutably
}
pub fn example_fn_mut() -> i32 {
let mut counter = 0;
call_fn_mut(|| {
counter += 1;
counter
}) // Mutably borrows counter
}
pub fn example_fn_once() -> String {
let s = String::from("owned");
call_fn_once(|| s) // Moves s
}
/// All closures implement FnOnce, some also implement FnMut, some also implement Fn
pub fn closure_hierarchy() {
let x = 10;
let f: &dyn Fn() -> i32 = &|| x;
let _: &dyn FnMut() -> i32 = f; // Fn implies FnMut
let _: &dyn FnOnce() -> i32 = f; // FnMut implies FnOnce
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fn() {
assert_eq!(example_fn(), 20);
}
#[test]
fn test_fn_mut() {
assert_eq!(example_fn_mut(), 3); // 1 + 2
}
#[test]
fn test_fn_once() {
assert_eq!(example_fn_once(), "owned");
}
#[test]
fn test_fn_pointer() {
fn add_one(x: i32) -> i32 {
x + 1
}
let fp: fn(i32) -> i32 = add_one;
assert_eq!(fp(41), 42);
}
}Key Differences
FnOnce prevents double-calling at compile time; OCaml must use runtime guards (ref bool) for the same constraint.mut f requirement**: Rust's call site must declare mut f for FnMut callers; OCaml has no such requirement.Box<dyn Fn()>, Box<dyn FnMut()>, and Box<dyn FnOnce()> are distinct types in Rust; OCaml uses a single function type.Fn: FnMut: FnOnce subtype coercions happen at compile time via trait coherence; OCaml has no equivalent.OCaml Approach
OCaml functions are first-class but have no trait hierarchy — all closures are uniformly applicable:
let call_fn (f: unit -> int) = f () + f ()
let call_fn_mut (f: unit -> int) = f () + f () (* same signature *)
(* "FnOnce" semantics must be enforced manually *)
let call_once f =
let called = ref false in
fun () -> if !called then failwith "called twice"
else (called := true; f ())
OCaml has no type-level distinction between a closure that mutates, one that doesn't, or one that should only be called once.
Full Source
#![allow(clippy::all)]
//! # Fn FnMut FnOnce — Closure Traits
/// Fn: can be called multiple times, doesn't mutate state
pub fn call_fn<F: Fn() -> i32>(f: F) -> i32 {
f() + f()
}
/// FnMut: can be called multiple times, may mutate state
pub fn call_fn_mut<F: FnMut() -> i32>(mut f: F) -> i32 {
f() + f()
}
/// FnOnce: can only be called once, consumes captured values
pub fn call_fn_once<F: FnOnce() -> String>(f: F) -> String {
f()
}
pub fn example_fn() -> i32 {
let x = 10;
call_fn(|| x) // Borrows x immutably
}
pub fn example_fn_mut() -> i32 {
let mut counter = 0;
call_fn_mut(|| {
counter += 1;
counter
}) // Mutably borrows counter
}
pub fn example_fn_once() -> String {
let s = String::from("owned");
call_fn_once(|| s) // Moves s
}
/// All closures implement FnOnce, some also implement FnMut, some also implement Fn
pub fn closure_hierarchy() {
let x = 10;
let f: &dyn Fn() -> i32 = &|| x;
let _: &dyn FnMut() -> i32 = f; // Fn implies FnMut
let _: &dyn FnOnce() -> i32 = f; // FnMut implies FnOnce
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fn() {
assert_eq!(example_fn(), 20);
}
#[test]
fn test_fn_mut() {
assert_eq!(example_fn_mut(), 3); // 1 + 2
}
#[test]
fn test_fn_once() {
assert_eq!(example_fn_once(), "owned");
}
#[test]
fn test_fn_pointer() {
fn add_one(x: i32) -> i32 {
x + 1
}
let fp: fn(i32) -> i32 = add_one;
assert_eq!(fp(41), 42);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fn() {
assert_eq!(example_fn(), 20);
}
#[test]
fn test_fn_mut() {
assert_eq!(example_fn_mut(), 3); // 1 + 2
}
#[test]
fn test_fn_once() {
assert_eq!(example_fn_once(), "owned");
}
#[test]
fn test_fn_pointer() {
fn add_one(x: i32) -> i32 {
x + 1
}
let fp: fn(i32) -> i32 = add_one;
assert_eq!(fp(41), 42);
}
}
Deep Comparison
Fn Fnmut Fnonce: Comparison
See src/lib.rs for the Rust implementation.
Exercises
fn retry<F: FnMut() -> bool>(mut f: F, n: usize) -> bool that calls f up to n times and returns true on the first success.once adapter**: Write fn once<F: FnOnce() -> T, T>(f: F) -> impl FnMut() -> Option<T> that wraps a FnOnce in a FnMut that returns Some(result) on first call and None thereafter.dyn Fn(i32)->i32 (dynamic dispatch via vtable) against impl Fn(i32)->i32 (monomorphised static dispatch) for 10 million calls using criterion.