Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type of a function pointer to method with lifetime parameters

Tags:

rust

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?

like image 515
Adrian Willenbücher Avatar asked Oct 27 '25 08:10

Adrian Willenbücher


1 Answers

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)];
like image 161
trent Avatar answered Oct 30 '25 07:10

trent



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!