Impl trait in function pointer

fn main() {
    let f = foo;
}
trait T{}
fn foo(t: impl T){
}

I think I understand why this isn't possible, since foo is really a generic function.

What I'm trying to do is understand the resulting compiler error; specifically the recommendation for a type annotation:

  Compiling playground v0.0.1 (/playground)
error[E0282]: type annotations needed for `fn(impl T) {foo::<impl T>}`
 --> src/main.rs:2:13
  |
2 |     let f = foo;
  |         -   ^^^ cannot infer type for type parameter `impl T` declared on the function `foo`
  |         |
  |         consider giving `f` the explicit type `fn(impl T) {foo::<impl T>}`, where the type parameter `impl T` is specified

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

You can't specify a type argument for impl functions since that's the nature of impl types. However, you can define a wrapper function if it's defined externally as follows:

fn foo(x: impl T) {}

fn bar<Z: T>(x: Z) {
    foo(x)
}

Which you can then use the turbofish syntax to specify the generic type parameter on.

I understand how to work around it, I just don't understand the type annotation.

fn(impl T) {foo::<impl T>}

I believe the recommendation is to use a function pointer type:

fn main() {
    let f: fn(i32) = foo;
}

however the other solution is better, as that allows you to use function items instead of function pointers.

1 Like

Kinda... I like to think of it as the ability to name a type. For example fn foo<T: Trait>(arg: T) pulls the type of arg out into a type variable we can use to refer to later on, whereas fn foo(arg: impl Trait) just says arg's type implements Trait, but we have no way of knowing what type it is or referring to that type.

The underlying reason why assigning let f = foo doesn't work is because a generic function doesn't actually exist as machine code, so there's no function to create a pointer to. You need to tell the compiler to "instantiate" the generic function (i.e. create a copy that works with some concrete type).

Yeah... That suggestion is misleading and should be reported as a bug.

What it's trying to say is the item foo is still generic and to store it in a variable it needs to sub in all the type variables (i.e. figure out what type impl T actually is).

The {foo::<impl T>} bit in the error message is often used to let people know the difference between a function and a function pointer. In diagnostics, a function pointer will just be represented using its signature, but an actual function will often have some { ... } bit to indicate the name is also associated with a particular implementation. So fn(impl T) {foo::<impl T>} is the compiler's attempt to describe foo's type (it's a function accepting impl T with an associated body, not just a pointer to such a function).

As a side note, in Rust a function can be thought of as a zero-sized struct which has a Fn trait impl, hence the distinction between a function and a function pointer. That's one of the reasons iterators optimise so well, every use of map() or filter() will know exactly which code it uses because the methods are generic and a different version will be constructed for every different closure type.

1 Like

Note that the thing Michael calls "functions" as opposed to "function pointers" are officially known as function items.

1 Like