Define `fn` type for methods that take `self: Foo<'a>`

Can you help me figure out how to define an fn type that can take values of method type?

struct Foo<'a>(&'a u32);

impl<'a> Foo<'a> {
    fn bar(&mut self) {
        type Func = fn(&mut Foo); // Same error with this
        //type Func = for<'b> fn(&'b mut Foo<'_>); // Same error with this
        let f: Func = Foo::baz;
        f(self)
    }
    
    fn baz(&mut self) {}
}
error[E0308]: mismatched types
 --> src/main.rs:7:23
  |
7 |         let f: Func = Foo::baz;
  |                ----   ^^^^^^^^ one type is more general than the other
  |                |
  |                expected due to this
  |
  = note: expected fn pointer `for<'a, 'b> fn(&'a mut Foo<'b>)`
                found fn item `for<'a> fn(&'a mut Foo<'_>) {Foo::<'_>::baz}`

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

Playground

The complicating factor here happens to be that the self type has a lifetime parameter, and the Funcs above are interpreted as for<'a, 'b> fn(&'a mut Foo<'b>).

In addition to the solution, I'd appreciate an understanding of the problem and the precise distinction the compiler is making.

You can think of it as if there's a distinct function per the lifetime in Foo<'_>.

The path to the function is

<Foo<'a>>::baz

for some lifetime 'a, and the type of baz satisfies

TypeOfBazWithA: for<'x> Fn(&'x mut Foo<'a>)

and <Foo<'b>>::baz would satisfy for<'x> Fn(&'x mut Foo<'b>) and so on, but none of these satisfy

: for<'x, 'y> Fn(&'x mut Foo<'y>)

// Needed to coerce to `fn(&mut Foo)`, which is the same as
// `for<'x> fn(&'x mut Foo<'_>)`, which is the same as
// `for<'x, 'y> fn(&'x mut Foo<'y>)

For that you would need the inner lifetime to be a parameter of the function:

fn baz<'x, 'y>(this: &'x mut Foo<'y>) {}
// Same thing:
// fn baz(this: &mut Foo<'_>) {}

...but then you can't use self .


How to fix it? If you only need to be able to pass in a self: &mut Self -- one particular inner lifetime 'a within the context of impl<'a> Foo<'a> -- you can change the Func type alias.

    fn bar(&mut self) {
        type Func<'unrelated> = fn(&mut Foo<'unrelated>);
        let f: Func = Foo::baz;
        f(self)
    }

Or if you do want to be able to take any inner lifetime, you can wrap a closure around the baz method.

    fn bar(&mut self) {
        type Func = fn(&mut Foo);
        let f: Func = |s| Foo::baz(s);
        f(self)
    }
1 Like