'Global function pointer to a method errors "mismatched types"

I want to make a global function pointer pointing to a class method, so I did something like the following minimal reproducible example:

struct Foo<'a> {
    data: &'a str,
}

impl<'a> Foo<'a> {
    pub fn foo(&self) {
        println!("{}", self.data);
    }
}

type FooFn = fn(&Foo);

const FUNC: FooFn = Foo::foo;

fn main() {
    let data = String::from("hello");
    let foo = Foo { data: &data };
    FUNC(&foo);
}

Rust Playground

But rustc gives me this error:

$ rustc test.rs
error[E0308]: mismatched types
  --> test.rs:13:21
   |
13 | const FUNC: FooFn = Foo::foo;
   |                     ^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'r, 's> fn(&'r Foo<'s>)`
              found fn pointer `for<'r> fn(&'r Foo<'_>)`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

What is this error trying to say? How can I fix it?



Solution 1:[1]

Function pointers in Rust can have early-bound and late-bound lifetime parameters. An early-bound lifetime parameter needs to be specified at the time the function pointer is created. A late-bound lifetime parameter is only specified when the function the pointer points to is called, and it can be different each time the function is called.

The function pointer type FooFn has two late-bound lifetime parameters. As spelled out in the error message, it's definition is equivalent to

let FooFn = for<'r, 's> fn(&'r Foo<'s>);

The type of Foo::foo is different, though. An associated function can only be looked up once the type it is associated with is fully specified. In other words, all lifetime parameters of the self type need to be early-bound. This means Foo::foo has one early-bound and one late-bound lifetime parameter. The lifetime parameter of Foo gets fixed at the time the function pointer is created, and the function pointer can only be called for that specific lifetime. The compiler denotes this yet-to-be-determined lifetime as '_ in the error message. (In this context, '_ would later be inferred to be 'static, since it's the only fixed lifetime that makes sense here, but that's not really relevant for this question.)

There are a few ways to work around this. One way is to make foo() a free function:

fn foo(foo: &Foo) {
    println!("{}", foo.data);
}

Another way is to wrap Foo::foo in a trivial closure:

const FUNC: FooFn = |f| Foo::foo(f);

This way, the function we reference is no longer an associated function, and all lifetime parameters can be late-bound.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 marc_s