I have a struct with a lifetime and some methods:
struct Ctrl<'a> {
    x: &'a i32,
}
impl<'a> Ctrl<'a> {
    fn foo(&self) -> i32 {
        *self.x + 1
    }
    fn bar(&self) -> i32 {
        *self.x + 2
    }
}
Now I want to store pointers to the methods in a slice, kind of like a look-up table:
const LIST: &[_] = &[Ctrl::foo, Ctrl::bar];
Rust demands to know the slice element type and suggests for<'r> fn(&'r Ctrl) -> i32, but this results in an error:
error[E0308]: mismatched types
  --> main.rs:16:48
   |
16 | const LIST: &[for<'r> fn(&'r Ctrl) -> i32] = &[Ctrl::foo, Ctrl::bar];
   |                                                ^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'s, 'r> fn(&'r Ctrl<'s>) -> _`
              found fn pointer `for<'r> fn(&'r Ctrl<'_>) -> _`
Is there a way to specify the correct type?
The problem is that foo is a method of Ctrl<'x> for some specific lifetime 'x, and not a method generic over all Ctrl<'_>. If you try to make all the lifetimes explicit, you will find that some of them can't be expressed:
const LIST: &[for<'a, 'b> fn(&'b Ctrl<'a>) -> i32] = &[Ctrl::<'?>::foo, Ctrl::<'?>::bar)];
//                                          What lifetimes?  ^^^^             ^^^^
Since foo isn't generic, but is bound to a specific Ctrl<'_>, there's no way to express the concept you need. You might try something like <for<'a> Ctrl<'a>>::foo, but that's not legal in current Rust.
One way to fix this would be to wrap each method in a closure that can be coerced to a function pointer:
const LIST: &[fn(&Ctrl<'_>) -> i32] = &[|this| this.foo(), |this| this.bar()];
This might get a little verbose if you have a lot of methods or if the signatures are more complex. Another option is to wrap each method in a free function with the correct signature. You could write a declarative macro to make it easier:
/// Wrap `Ctrl::$method` in a free function and return it.
macro_rules! ctrl {
    ($method:ident) => {{
        fn inner(this: &Ctrl<'_>) -> i32 {
            this.$method()
        }
        inner
    }};
}
const LIST: &[fn(&Ctrl<'_>) -> i32] = &[ctrl!(foo), ctrl!(bar)];
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With