Closure Type Inference
Unlike named functions (fn), which require explicit parameter and return type annotations because they form part of a module or public API boundary, closures infer their parameter and return types from their body and how they are first used within a function.
How the Compiler Infers Closure Types
The compiler determines a closure's signature from two sources:
- Target context: Passing a closure to a function or iterator adapter with a known signature (such as
Iterator::maporOption::unwrap_or_else) immediately constrains the parameter and return types. - First call site: If a closure is bound to a local variable without annotations, the first call to that closure locks in its concrete parameter and return types.
fn main() {
// 1. Inferred from iterator context (x is inferred as &i32):
let doubled: Vec<i32> = [1, 2, 3].iter().map(|x| x * 2).collect();
// 2. Inferred from the first call site (s is locked to &str):
let identity = |s| s;
let word = identity("infer");
println!("Doubled: {doubled:?}, word: {word}");
}
Doubled: [2, 4, 6], word: infer
Type Lock-In After First Use (E0308)
An unannotated closure is not polymorphic across multiple types. Once the compiler infers a concrete type for a closure's parameter from its first use, calling the same closure with a different type fails with E0308:
// ❌ does not compile
fn main() {
let echo = |x| x;
let s = echo(String::from("text")); // Locks `x` to `String`
let n = echo(42); // Error: expected `String`, found integer
}
error[E0308]: mismatched types
--> src/main.rs:6:18
|
6 | let n = echo(42);
| ---- ^^ expected `String`, found integer
| |
| arguments to this function are incorrect
|
note: closure parameter defined here
--> src/main.rs:3:17
|
3 | let echo = |x| x;
| ^
Unused Unannotated Closures (E0282)
If a closure is declared without type annotations and is never called or passed to a typed consumer, the compiler cannot infer its parameter types and emits E0282:
// ❌ does not compile
fn main() {
let uncalled = |x| x;
}
error[E0282]: type annotations needed
--> src/main.rs:3:21
|
3 | let uncalled = |x| x;
| ^
|
help: consider giving this closure parameter an explicit type
|
3 | let uncalled = |x: /* Type */| x;
| ++++++++++++
Unique Anonymous Compiler Types
Every closure expression in source code has its own unique, unnameable compiler-generated type, even if two closures have the exact same parameter types, return type, and body. Consequently, you cannot assign one closure variable to another without boxing (Box<dyn Fn(...)>) or coercing non-capturing closures to function pointers (fn(...)).