Array of method references: "cannot infer an appropriate lifetime"

I have a const array FUNCS which holds references to some methods in a struct MyStruct<'a>. I want to use this const array inside a method of MyStruct, but I get the error "cannot infer an appropriate lifetime due to conflicting requirements". There are some conflicts between 'a and 'static, but I can't solve it.

If I use the const array outside the struct implementation (eg in main()), it's working.

How can I use this array inside the struct implementation?

Code:

type Func<'a> = fn(&MyStruct<'a>);

const FUNCS: [Func; 2] = [
    MyStruct::one,
    MyStruct::two,
];

struct MyStruct<'a> {
    s: &'a str,
}

impl<'a> MyStruct<'a> {
    fn use_func(&self, n: usize) {
        let f = FUNCS[n]; // error
        f(self);
    }

    fn one(&self) {
        println!("{}", self.s.len());
    }

    fn two(&self) {
        println!("{}", self.s.len() * 2);
    }
}

fn main() {
    let p = MyStruct { s: "hi" };

    p.use_func(1);

    // If use_func() is commented, the code below is working:
    let f = FUNCS[0];
    f(&p);
}

Error report:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:14:17
   |
14 |         let f = FUNCS[n];
   |                 ^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 12:6...
  --> src/main.rs:12:6
   |
12 | impl<'a> MyStruct<'a> {
   |      ^^
note: ...so that the expression is assignable
  --> src/main.rs:15:11
   |
15 |         f(self);
   |           ^^^^
   = note: expected `&MyStruct<'_>`
              found `&MyStruct<'a>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
  --> src/main.rs:14:17
   |
14 |         let f = FUNCS[n];
   |                 ^^^^^^^^
   = note: expected `for<'r> fn(&'r MyStruct<'_>)`
              found `for<'r> fn(&'r MyStruct<'static>)`

For more information about this error, try `rustc --explain E0495`.
error: could not compile `basic-test` due to previous error

You can make this work with higher-ranked trait bounds:

type Func = for<'a> fn(&MyStruct<'a>);

const FUNCS: [Func; 2] = [
    |s| s.one(),
    |s| s.two(),
];

Edit: In your original formulation, the line

const FUNCS: [Func; 2] = ...

is shorthand for

const FUNCS: [Func<'static>; 2] = ...

because constant values must be 'static. This means the functions stored there will only work when MyStruct is storing &'static strs, which is the case in your main function.

use_func, on the other hand, needs to work even if the strings stored inside the object are temporary. You can get a similar error in main if you store a borrowed string instead of a string literal:

fn main() {
    let s = String::from("hi");
    let p = MyStruct { s: s.as_str() };

    let f = FUNCS[0];
    f(&p);
}
error[E0597]: `s` does not live long enough
  --> src/main.rs:24:27
   |
24 |     let p = MyStruct { s: s.as_str() };
   |                           ^---------
   |                           |
   |                           borrowed value does not live long enough
   |                           argument requires that `s` is borrowed for `'static`
...
29 | }
   | - `s` dropped here while still borrowed
3 Likes

Thanks you for your answer, indeed it works better like this :+1:.

But by using closures in the array, doesn't it add one level of indirection, then a little runtime cost?

There's no object associated with those closures because they're being coerced into function pointers. As the functions don't do any work except for calling another function with the same arguments, I'd be quite surprised if they don't get optimized away completely.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.