Cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements

I'm trying to understand/solve the following error related to lifetimes (I'm new to rust so please bear with me).

The error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/tui/screens/accounts.rs:42:59
   |
42 |             self.account_table.items = state.eth_accounts.iter().map(|a| (a.get(), 0.0)).collect();
   |                                                           ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 29:16...
  --> src/tui/screens/accounts.rs:29:16
   |
29 |         state: &mut AppState, 
   |                ^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/tui/screens/accounts.rs:42:40
   |
42 |             self.account_table.items = state.eth_accounts.iter().map(|a| (a.get(), 0.0)).collect();
   |                                        ^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 23:6...
  --> src/tui/screens/accounts.rs:23:6
   |
23 | impl<'a> Drawable for Accounts<'a> {
   |      ^^
note: ...so that the expression is assignable
  --> src/tui/screens/accounts.rs:42:40
   |
42 |             self.account_table.items = state.eth_accounts.iter().map(|a| (a.get(), 0.0)).collect();
   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Vec<(&'a str, _)>`
              found `Vec<(&str, _)>`

So at a high level what I'm reading is: on line 42 I'm trying to assign something that has a lifetime mismatch between lefthand side and righthand side.

In particular, it seems that the righthand side contains a &str with no lifetime parameter, while the lefthand side expects a &'a str. Lefthand side makes total sense to me given I have a struct elsewhere in the code which looks like this:

pub struct StatefulTable<'a> {
    pub state: TableState,
    pub items: Vec<(&'a str, f64)>,
}

Now the righthand side is confusing. I think I get why there's no lifetime parameter there - it's because it's derived from state: &mut AppState which also has no lifetime parameter. But beyond that I'm struggling with the below questions:

Question 1 - why can't rust automatically assign 'a to the &'str it gets back from the Hashmap's .get() method?

Like the compiler says at the end of the error message, it knows it wants a Vec<(&'a str, _)> - so why not auto-assign 'a to the &str it gets from the hashmap?

Question 2 - why do both of the below attemps fail to solve the error?

I've tried manually massaging the righthand side into the appropriate format, but both of these fail:

self.account_table.items: Vec<(&'a str, f64)> = state.eth_accounts.iter().map(|a| (a.get(), 0.0)).collect();
self.account_table.items = state.eth_accounts.iter().map(|a| (a.get(), 0.0)).collect::<Vec<(&'a str, f64)>>();

Question 3 - what's the idiomatic way to solve this?

I basically have a global state without a lifetime parameter (line 29) and I'm trying to assign something from it to a local state with the 'a lifetime parameter. Should I:

  • add 'a to global state? Feels excessive as I would have to go and mark up <'a> in most of the codebase.
  • use lifetime comparisons? If so, then how?
  • somehow massage the anonymous lifetime into 'a? My 2 approaches above failed. Is there a third?

Hope this is specific/answerable enough. Lifetime errors are hard because they only arise when app gets complex enough and I always struggle to provide a "minimum reproducible example".

Question 1

The anonymous lifetime means it could be any lifetime the caller chooses. So the lifetime of state: &mut AppState might be shorter than 'a, and that lifetime cannot be extended. (Maybe the items get moved/deallocated/etc before 'a, and this would leave you with a dangling reference.)

Question 2

The method declaration defines the API, and you can't override this in the body. In this case, the API says "the caller can chose any lifetime they please" (the anonymous lifetime). If you want to force it to have to be longer, you have to do so with lifetime bounds in the method signature, not the body of the method.

Question 3

It's hard to give solid advice without the code to refer to, but you could perhaps

  • Restrict the lifetime of the &mut AppState with lifetime bounds
  • Modify your program to not store temporary references is your struct (get rid of the 'a lifetime parameter on StatefulTable)

Lifetime bounds are the only sound way to "massage" the currently-anonymous lifetime.

1 Like

Thanks for commenting!

After some more digging I figured out that indeed the reference to state is shorter-lived than the reference to self.accounts_table.items. Therefore it makes sense that during this assignment:

self.account_table.items = state.eth_accounts.iter().map(|a| (a.get(), 0.0)).collect();

the compiler is unhappy. I'm trying to assign something with a shorter lifetime (righthand side) to something with a longer lifetime (lefthand side).

Assuming that I can't change the lifetime on state, is my only options to get rid of &str inside of Accounts and replace with String? Like so:

pub struct StatefulTable<'a> {
    pub state: TableState,
    pub items: Vec<(String, f64)>,
}

Or am I overlooking other options?

The ful code (using String) is here, in case it's helpful. I'm forced to turn &str into String on line 36.

Thanks a ton in advance.

There are other options, like Rc<String> or Arc<String>. Or maybe some rearrangement of logic, I didn't dig terribly deep. If there's no reason not to, though, just cloning the Strings isn't a bad place to start. (You would also drop the 'a parameter on StatefulTable.)

You usually don't want a lifetime parameter on a struct that's not a temporary borrow in some sense or another.

1 Like

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.