Why `Vec<impl Fn>` is allowed in function signatures

I know why Vec<impl Fn> is not allowed in let bindings because the size of impl Fn is unknown at compile time - the compiler needs to know how much memory is required to be allocated for this array.
But why is it allowed in function signatures?

E.g.

fn run_funs(funs: Vec<impl Fn()>) {
    for f in funs {
        f();
    }
}

fn f1() {
    println!("in f1()");
}

fn f2() {
    println!("in f2()");
}

fn main() {
    run_funs(vec![f1, f2]);
}

(play ground)

Does this vec become a heterogeneous array but somehow doesn't require its items having a uniformed structure, like a pointer?

There's nothing fundamentally challenging about Vec<impl Fn()>. impl Trait is basically an anonymous generic, so this is just the type of a Vec that contains any quantity of the same type that implements Fn(). In your code, both f1 and f2 become fn() pointers, so they're the same type.

This doesn't work in let bindings because it's still in development. The error message links an issue with more info:

error[E0562]: `impl Trait` is not allowed in the type of variable bindings
 --> src/main.rs:2:16
  |
2 |     let f: Vec<impl Fn()> = vec![|| {}];
  |                ^^^^^^^^^
  |
  = note: `impl Trait` is only allowed in arguments and return types of functions and methods
  = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information
2 Likes

Adding to what @drewtato said: This isn't true. The size of impl Fn (and every other impl ??? type) is known at compile time. You may be thinking of dyn Fn, which size isn't known at compile time.

1 Like

You can use impl ?Sized + Trait to disable the implicit Sized bound. (But yeah, the instability of impl Trait in let binding is unrelated to any of that.)

1 Like

Still, Vec<impl ?Sized> is not allowed because Vec<T> has an implicit T: Sized.

Yes, but it is not allowed in function signatures, not just let bindings.[1] So the "has-Sized-bound-or-not-ness" of any given impl ??? is still orthogonal to whether it is allowed in let bindings...

...in contrast to what was written in the OP.


  1. And impl Trait + Sized + ?Sized is allowed, albeit pointless, except in macros perhaps (think impl $tr + ?Sized with $tr = Clone) â†Šī¸Ž

1 Like