The FnOnce Trait
The FnOnce trait in std::ops is the base trait implemented by every closure and function, representing a callable that consumes itself by value (self) when invoked and therefore is only guaranteed to be callable once.
Conceptual Definition
pub trait FnOnce<Args> {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
Because FnOnce::call_once takes self by value, a closure that moves a non-Copy value out of its captured environment can safely return or drop that value during its single execution without leaving the closure struct in a partially moved, invalid state for a second call.
Closures That Only Implement FnOnce
A closure implements only FnOnce (and neither FnMut nor Fn) when its body moves ownership out of a captured variable:
fn execute_task<F, T>(task: F) -> T
where
F: FnOnce() -> T,
{
task()
}
fn main() {
let buffer = vec![10, 20, 30];
// `.into_iter()` consumes `buffer` by value, so `sum_and_drop` is `FnOnce` only:
let sum_and_drop = || {
buffer.into_iter().sum::<i32>()
};
let result = execute_task(sum_and_drop);
println!("Result: {result}");
}
Result: 60
Calling an FnOnce-Only Closure Twice Fails (E0382)
Because invoking an FnOnce-only closure consumes the closure value on the first call, calling it a second time fails with a use-after-move error (E0382):
// ❌ does not compile
fn main() {
let name = String::from("ephemeral");
let consume_name = || {
drop(name);
};
consume_name();
consume_name(); // Error: `consume_name` was moved and consumed in the first call
}
error[E0382]: use of moved value: `consume_name`
--> src/main.rs:10:5
|
5 | let consume_name = || {
| ------------ move occurs because `consume_name` has type `{closure@src/main.rs:5:24: 5:26}`, which does not implement the `Copy` trait
...
9 | consume_name();
| -------------- `consume_name` moved due to this call
10 | consume_name();
| ^^^^^^^^^^^^ value used here after move
|
note: closure cannot be invoked more than once because it moves the variable `name` out of its environment
--> src/main.rs:6:14
|
6 | drop(name);
| ^^^^
Standard Library APIs Using FnOnce
Single-shot callbacks in the standard library always bound their parameter by FnOnce so callers can freely move owned values out of their environment:
Option::unwrap_or_else(f: impl FnOnce() -> T)Option::map(f: impl FnOnce(T) -> U)Result::map_err(op: impl FnOnce(E) -> F)std::thread::spawn(f: impl FnOnce() -> T + Send + 'static)