Confused by lifetime error messages? Tell me about it!

I created an issue regarding this specific error message: https://github.com/rust-lang/rust/issues/16948

2 Likes

Wenderen was having some troubles with cloning &str the other day.

https://gist.github.com/wenderen/2efdea371b2ac7add6ae

Here, a Vec<&str> is being cloned. Usually a clone solves all lifetime issues, but &str isnā€™t Deref, so the pointer (instead of the data) seems to be cloned, giving rise to a lifetime error.

This isnā€™t exactly a problem with the lifetime system, but it would be nice to have a cleaner set of error messages for &str. Mostly one should be using String there, but thatā€™s not entirely clear (perhaps clearer instructions on when to use String and &str in the docs might help?). &str looks too much like &T, but it doesnā€™t behave the same.

Just to add to this post, the error is on line 13 in the gist, where we do inputs.push(split_input.clone());.

Sorry if someone has already given this example, Iā€™m not event sure if it is a valid example but it seems like it should be allowed somehow and I canā€™t figure it out:

trait SomeTrait {
    fn say_hello(&self);
}

struct SomeStruct;

impl SomeTrait for SomeStruct {
    fn say_hello(&self) {
        println!("EH")
    }
}

struct AnotherStruct {
    some_trait: Box<SomeTrait>
}

Results in:

rust.workspace/learn/src/main.rs:14:21: 14:30 error: explicit lifetime bound required rust.workspace/learn/src/main.rs:14 some_trait: Box

1 Like

Somone just posted this gem on irc:

use std::collections::HashSet;

fn grab_words(
    keys: &Vec<char>
    ) -> Vec<String>{
    let mut keys_string = String::new(); 
    
    for c in keys.iter(){
        keys_string.push_char(*c);
    }
    
    keys_string.as_slice().split(' ').
        map(|x| String::from_str(x)).
        collect::<Vec<String>>()
}

fn evaluate_words(
    keys: &Vec<char>,
    word_list: &HashSet<&str>,
    ) -> f64 {
    let words = grab_words(keys);

    words.iter().fold(0.0f64, |err, w|{
        if word_list.contains(&w.as_slice()){
            err
        } else {
            err - 3.
        }
    }) / words.len() as f64
}

fn main(){
}
<anon>:23:5: 23:10 error: `words` does not live long enough
<anon>:23     words.iter().fold(0.0f64, |err, w|{
              ^~~~~
<anon>:20:14: 30:2 note: reference must be valid for the anonymous lifetime #3 defined on the block at 20:13...
<anon>:20     ) -> f64 {
<anon>:21     let words = grab_words(keys);
<anon>:22 
<anon>:23     words.iter().fold(0.0f64, |err, w|{
<anon>:24         if word_list.contains(&w.as_slice()){
<anon>:25             err
          ...
<anon>:20:14: 30:2 note: ...but borrowed value is only valid for the block at 20:13
<anon>:20     ) -> f64 {
<anon>:21     let words = grab_words(keys);
<anon>:22 
<anon>:23     words.iter().fold(0.0f64, |err, w|{
<anon>:24         if word_list.contains(&w.as_slice()){
<anon>:25             err
          ...
error: aborting due to previous error

playpen

I got this one:

pub fn get_configuration(url: String) -> Box {

src/olib/config/mod.rs:13:46: 13:60 error: explicit lifetime bound required
src/olib/config/mod.rs:13 pub fn get_configuration(url: String) -> Box<OConfiguration>  {
                                                                                                     ^~~~~~~~~~~~~~

Anyone know what is wrong? Iā€™d like to return a trait object that implements the trait OConfigurationā€¦

Edit:

Changing to

pub fn get_configuration(url: String) -> Box<OConfiguration+'static>  {

fixed itā€¦

On Thu, Sep 18, 2014 at 11:50:06PM +0000, Gankro wrote:

This example is quite confusing to me for two reasons.

Letā€™s first look at the error,

reference must be valid for the lifetime 'a as defined on the block at 26:67 but borrowed value is only valid for the block at 26:67

At first glance it appears that the compiler is saying ā€œyou gave me x but I wanted xā€ where x is ā€œthe block at 26:67ā€. On second glance one might notice that the compiler is trying to tell you the reason why it wants the reference to be valid for lifetime 'a (is this interpretation correct?). The wording of the error may or may not be clear to the acclimated reader, but either way the distinction between the use of x in what the compiler expected and what I provided is a bit subtle.

Now letā€™s consider the issue itself. The code in question is attempting to return a reference a long-lived data structure (&SuffixTree<E>) through an acess to a local structure defined in fn find_matches (namely cursors). If I change &**cur to simply &dict (which also has lifetime 'a) then the code compiles, so clearly 'a is not the issue.

At this point I was quite stumped. Thanks for shep on #rust we eventually determined that deref() was the issue. While itā€™s not clear from the impl the lifetime the reference returned by deref is that of the object being derefā€™d. This however means that in using deref the lifetime of the SuffixTree reference was artificially shortened to the lifetime of the Cursor that I got the reference through.

In hindsight this all seems reasonable enough; this is probably an abuse of Deref anyways (it would be nice to hear confirmation of this). Ultimately I ended up adding a fn get(&self) -> &'a SuffixTree<E> to Cursor<'a, E>. Iā€™m not sure what the compiler could do to better assist me in tracking down this issue.

Thanks!

1 Like

What happened to this reply? Is it really as empty as it appears?

Itā€™s a bug in the reply-via-email feature of Discourse. They require that you have your full comment at the top. If you try to inline your response, it just eats everything.

This one has me stumped today. First, the ā€œconsider using an explicit lifetime as shownā€ is what I already am doing. Second, the ā€˜expectedā€™ and ā€˜foundā€™ types are showing as identical.

src/db/query.rs:35:9: 38:6 note: consider using an explicit lifetime parameter as shown: fn add_selection<'a>(&mut self, sql: Sql<'a>)
src/db/query.rs:35     pub fn add_selection<'a>(&mut self, sql: Sql<'a>)
src/db/query.rs:36     {
src/db/query.rs:37         self.selections.push(sql);
src/db/query.rs:38     }
src/db/query.rs:37:30: 37:33 error: mismatched types: expected `db::query::Sql<'a>`, found `db::query::Sql<'a>` (lifetime mismatch)
src/db/query.rs:37         self.selections.push(sql);
                                                ^~~
src/db/query.rs:37:30: 37:33 error: mismatched types: expected `db::query::Sql<'a>`, found `db::query::Sql<'a>` (lifetime mismatch)
src/db/query.rs:37         self.selections.push(sql);
1 Like

Thanks, this is another helpful suggestion. Iā€™d need to see more context, but almost certainly what is happening is that 'a is already in scope at the impl or type level.

I encountered the same problem as @mikedilger above, hereā€™s the example in context: https://gist.github.com/maxsnew/9d3d30b469e7e39909d0

The bad error message is actually non-deterministic though! Compiling the buggy version with rustc --crate-type lib sometimes gives the good error message and sometimes the bad one. The only issue I could find like this is marked fixed: https://github.com/rust-lang/rust/issues/13057

https://github.com/PistonDevelopers/event/issues/202

I tried adding 'a in intuitive places to no avail =(. Any ideas?

Edit: Turns out mindlessly adding lifetimes wasnā€™t the solution - bvssvni switched Any to Ptr in intelligent places (because Any requires static lifetimes) and fixed the issue!

Thanks for having this thread!

Whoops, I accidentally put the same error message twice. Fixed to show the two different error messages I get (non-deterministically).

Also Iā€™m only getting the non-deterministic behavior on linux, on my mac Iā€™m always getting the bad error message.

It wasnā€™t clear to me how to make a Box field for a trait type. For example:


trait A {
    fn f(&self);
}

struct S {
    a : Box<A>
}

a.rs:6:13: 6:14 error: explicit lifetime bound required
a.rs:6     a : Box<A>

I took me a while to figure out that what 'static has to do with my type.

Real life code is here https://github.com/Djuffin/rusty-chess/blob/master/search.rs

If struct doesnā€™t accept lifetime as a parameter, then 'static is pretty much the only option, you could mention it in the error message.

struct MyMapWrapper<'mw>(pub HashMap<&'mw str, &'mw str>);

impl<'mw, S: Decoder<E>, E> Decodable<S, E> for MyMapWrapper<'mw> {
    fn decode(decoder: &mut S) -> Result<MyMapWrapper<'mw>, E> {
        decoder.read_map(|decoder, len| {
            let mut data_map: HashMap<&'mw str, &'mw str> = HashMap::new();
            for i in range(0u, len) {
                data_map.insert(match decoder.read_map_elt_key(i, |decoder| { decoder.read_str() }) {
                                    Ok(key) => key.as_slice(), Err(err) => return Err(err)
                                },
                                match decoder.read_map_elt_val(i, |decoder| { decoder.read_str() }) {
                                    Ok(val) => val.as_slice(), Err(err) => return Err(err)
                                });
            }
            Ok(MyMapWrapper(data_map))
        })
    }
}

playpen

Reports:

<anon>:17:48: 17:51 error: `key` does not live long enough
<anon>:17                                     Ok(key) => key.as_slice(), Err(err) => return Err(err)
                                                         ^~~
<anon>:12:64: 25:6 note: reference must be valid for the lifetime 'mw as defined on the block at 12:63...
<anon>:12     fn decode(decoder: &mut S) -> Result<MyMapWrapper<'mw>, E> {
          ...
<anon>:16:33: 18:34 note: ...but borrowed value is only valid for the match at 16:32
<anon>:16                 data_map.insert(match decoder.read_map_elt_key(i, |decoder| { decoder.read_str() }) {
<anon>:17                                     Ok(key) => key.as_slice(), Err(err) => return Err(err)
<anon>:18                                 },
<anon>:20:48: 20:51 error: `val` does not live long enough
<anon>:20                                     Ok(val) => val.as_slice(), Err(err) => return Err(err)
                                                         ^~~
<anon>:12:64: 25:6 note: reference must be valid for the lifetime 'mw as defined on the block at 12:63...
<anon>:12     fn decode(decoder: &mut S) -> Result<MyMapWrapper<'mw>, E> {
          ...
<anon>:19:33: 21:34 note: ...but borrowed value is only valid for the match at 19:32
<anon>:19                                 match decoder.read_map_elt_val(i, |decoder| { decoder.read_str() }) {
<anon>:20                                     Ok(val) => val.as_slice(), Err(err) => return Err(err)
<anon>:21                                 });

I think both these statements (reference must be valid for the lifetime 'mw as defined on the block atā€¦ & but borrowed value is only valid for the match) should also suggest something more clear (like the idea with try to store it with let var), because itā€™s hard to suggest what is the best way to do here w/o the help of compiler. I read it like it suggests me to somehow change the lifetime of key / val, while I canā€™t. At least I feel it so, and seems itā€™s open to share feelings like that in this thread :).

I tried this:

type ClosureAlias<T, U> = |T| -> U;

And the error is this:

lib.rs:39:27: 39:35 error: explicit lifetime bound required
lib.rs:39 type ClosureAlias<T, U> = |T| -> U;
                                    ^~~~~~~~

It would be very helpful to get a suggestion on what the syntax for the lifetime bound is.

Hereā€™s a confusing one:

use std::collections::HashMap;
struct WordGraph<'a> {
    g: HashMap<&'a str, Box<Vec<&'a str>>>,
}

impl <'a> WordGraph<'a> {
    fn new() -> WordGraph<'a> { WordGraph{g: HashMap::new(),} }
    fn insert_word<'a>(&self, s: &'a str) {
        if (!self.g.contains_key(&s)) { 
          self.g.insert(s, box Vec::new());
        }
    }
}

fn main() {
    let mut wg = WordGraph::new();
    let words = vec!("one" , "two" , "one" , "three" , "two" , "four");
    for w in words.iter() { wg.insert_word(*w); }
}

The compilerā€™s ā€œconsiderā€¦ā€ suggestion is less then helpful. From this playpen:

<anon>:10:14: 10:37 error: cannot infer an appropriate lifetime due to conflicting requirements
<anon>:10         if (!self.g.contains_key(&s)) { 
                       ^~~~~~~~~~~~~~~~~~~~~~~
<anon>:11:25: 11:26 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
<anon>:11           self.g.insert(s, box() Vec::new());
                                  ^
<anon>:9:5: 13:6 help: consider using an explicit lifetime parameter as shown: fn insert_word<'a>(&self, s: &'a str)
<anon>:9     fn insert_word<'a>(&self, s: &'a str) {
<anon>:10         if (!self.g.contains_key(&s)) { 
<anon>:11           self.g.insert(s, box() Vec::new());
<anon>:12         }
<anon>:13     }
error: aborting due to 2 previous errors

As you can see, the suggested function signature is exactly what I haveā€¦ I donā€™t have a solution yet. This is intended to be an adjacency-list representation of a graph where the nodes are slices into strings (or 'static &str refs) that are owned elsewhere, and where the lifetime of the references can be proven to outlive the HashMap/WordGraph itself.

I suspect the compiler message is a bug, but that aside, how would I solve this issue?

I believe the problem there is the shadowing of the lifetime 'a. The declaration on the method is creating a new independent lifetime separate from the 'a of WordGraph. Removing the <'a> from insert_word should fix it.

Shadowing of lifetimes and generics leads to confusing messages because two lifetimes/types have the same textual name but are different internally. In this case the compiler is presumably trying to suggest the other 'a leaving the declared one unused, I guess it should be stripping that declaration, or rename the method-local lifetime.

(Thereā€™s a few issues in this vein, but Iā€™m on my phone so I canā€™t find them easily, and I donā€™t think there are any that cover suggestions + shadowing specifically.)