Full type definition of closure


#1

I’m learning more about closures and lifetime/move semantics (coming from Scala) and I was wondering why the code below fails to compile (https://play.rust-lang.org/?gist=776b1a693bb649f864bcb2e99de613ce). The compile error is rather long but this is the interesting part (I think):

error[E0277]: the trait bound `for<'r> std::ops::Fn(&'r i32) -> i32: std::marker::Sized` is not satisfied
 --> <anon>:5:13
  |
5 |         let cl_inner: Fn(&i32) -> i32 = move |b| {
  |             ^^^^^^^^ the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::Fn(&'r i32) -> i32`
  |
  = note: `for<'r> std::ops::Fn(&'r i32) -> i32` does not have a constant size known at compile-time
  = note: all local variables must have a statically known size

If I remove the explicit type definition of cl_inner, i.e., changing let cl_inner: Fn(&i32) -> i32 = to let cl_inner = it works. Did I get the type wrong or is the compiler not able to interpret the closure properly? Thanks :slight_smile:

fn main() {
    let outer_values: Vec<i32> = vec![0; 10];

    let cl_outer = |a| {
        let cl_inner: Fn(&i32) -> i32 = move |b| {
            a + b
        };
        let res = (0..10).map(cl_inner);
        res
    };

    let result: Vec<i32> = outer_values.iter().flat_map(cl_outer).collect::<Vec<i32>>();

    println!("{:?}", result);
}

#2

This is because of the way Rust implements closures, which is somewhat counterintuitive but designed to maximize optimization potential. In short, the type of your closure is not Fn(&i32) -> i32. Rather, each closure expression has its own, unique, unnameable type, which implements the trait Fn(&i32) -> i32. However, if you write Fn(&i32) -> i32 as a type, Rust thinks you want a trait object (dynamic dispatch), which can only be used behind a pointer (reference, Box, etc.) because it doesn’t have a fixed size.


#3

Thanks! Now I also understand why passing closures into functions requires using generics with trait bounds :slight_smile: