cd /news/developer-tools/rust-guide-13-2-closure-type-inferen… · home topics developer-tools article
[ARTICLE · art-36097] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

[Rust Guide] 13.2. Closure Type Inference and Annotations

Rust closures, unlike functions, do not require explicit type annotations for parameters or return values because they are typically used in narrow contexts where the compiler can infer types. The compiler infers a single concrete type for each parameter and return value based on usage, and attempting to use a closure with different types results in a compile-time error.

read3 min views1 publishedJun 22, 2026

During its design, Rust drew inspiration from many languages, and functional programming had a particularly strong influence on Rust. Functional programming often includes passing functions as values to parameters, returning them from other functions, assigning them to variables for later execution, and so on.

In this chapter, we will discuss some Rust features that are similar to what many languages call functional features:

If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.

Unlike functions defined with fn

, closures do not require explicit type annotations for parameters or return values.

Functions must be explicit because they are part of a public interface exposed to users, and a clearly defined interface helps everyone agree on the parameter and return types.

Closures are not used as exposed interfaces. They are usually stored in variables, they do not need names when used, and they are not exposed to users of our codebase. Therefore, closures do not require explicit type annotations for parameters and return values.

Closures are also usually short and work only in a narrow context, so the compiler can often infer the types. Of course, you can still write the annotations manually if you want to.

Take a look at an example:

This is the version using a function definition:

fn simulated_expensive_calculation(intensity: u32) -> u32 {  
    println!("calculating slowly...");  
    thread::sleep(Duration::from_secs(2));  
    intensity  
}

This is the version using a closure:

let expensive_closure = |num:u32| -> u32 {  
    println!("calculating slowly...");  
    thread::sleep(Duration::from_secs(2));  
    num  
};

Explicit annotations are used here because there is no surrounding context for Rust to infer the types. If there is context, then they are not needed:

fn generate_workout(intensity: u32, random_number: u32) {  
    let expensive_closure = |num| {  
        println!("calculating slowly...");  
        thread::sleep(Duration::from_secs(2));  
        num  
    };  
    if intensity < 25 {  
        println!("Today, do {} pushups!", expensive_closure(intensity));  
        println!("Next, do {} situps!", expensive_closure(intensity));  
    } else {  
        if random_number == 3 {  
            println!("Take a break today! Remember to stay hydrated!");  
        } else {  
            println!("Today, run for {} minutes!", expensive_closure(intensity));  
        }  
    }  
}

The parameter num

does not need an explicit type annotation because the argument passed in the later call is intensity

, whose type is u32

, so Rust infers that num

is also u32

.

Here are four examples:

 fn add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;

{}

. Because it contains only one expression, the braces can be omitted.A closure’s definition will ultimately infer only one specific concrete type for its parameters and return value.

Take a look at an example:

    let example_closure = |x| x;

    let s = example_closure(String::from("hello"));
    let n = example_closure(5);

Output:

$ cargo run
   Compiling closure-example v0.1.0 (file:///projects/closure-example)
error[E0308]: mismatched types
 --> src/main.rs:5:29
  |
5 |     let n = example_closure(5);
  |             --------------- ^- help: try using a conversion method: `.to_string()`
  |             |               |
  |             |               expected `String`, found integer
  |             arguments to this function are incorrect
  |
note: expected because the closure was earlier called with an argument of type `String`
 --> src/main.rs:4:29
  |
4 |     let s = example_closure(String::from("hello"));
  |             --------------- ^^^^^^^^^^^^^^^^^^^^^ expected because this argument is of type `String`
  |             |
  |             in this closure call
note: closure parameter defined here
 --> src/main.rs:2:28
  |
2 |     let example_closure = |x| x;
  |                            ^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `closure-example` (bin "closure-example") due to 1 previous error

When the compiler sees the first call to the closure, it determines that both the input and output values are String

, so it locks in String

as the parameter and return type for this closure. That is why a later call with an integer causes an error.

── more in #developer-tools 4 stories · sorted by recency
── more on @rust 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/rust-guide-13-2-clos…] indexed:0 read:3min 2026-06-22 ·