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