Lifetime problem (s must live as long as 'a, but it does)


#1

I’ve got the following code:

    /// Helper method to create a lexer from a &str (or String etc.).
    pub fn from_str<'a>(s: impl AsRef<str> + 'a) -> Lexer<std::str::Chars<'a>> {
        let iter_ref = s.as_ref();
        let iter: std::str::Chars<'a> = iter_ref.chars();
        let lexer = Lexer {
            iter,
            pos: 0
        };
        lexer
    }

which won’t compile with the following error:

error[E0597]: `s` does not live long enough
  --> src/lexer.rs:24:24
   |
24 |         let iter_ref = s.as_ref();
   |                        ^ borrowed value does not live long enough
...
31 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the method body at 23:5...
  --> src/lexer.rs:23:5
   |
23 |     pub fn from_str<'a>(s: impl AsRef<str> + 'a) -> Lexer<std::str::Chars<'a>> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

but in the signature I explicitly say that s: 'a, so I don’t understand the error. Can anyone help?


#2

'a is a bound on the type of s, effectively saying its values are allowed to live that long (i.e., can’t contain any references to shorter-lived data). It doesn’t mean that any particular value of that type must live that long. A value lives only until it is reassigned or goes out of scope, in this case when the function returns.


#3

A concrete example where s does not live long enough:

fn main() {
    let _lexer = from_str(String::from("derekdreery"));
}

In your signature of from_str the type of s would be String which is 'static because String does not borrow data of any shorter lifetime than 'static, and yet the function shouldn’t be allowed to return Lexer<Chars<'static>> because the string is dropped before the function returns. This would be a use-after-free bug caught by the borrow checker.


#4

If you want to avoid the case where the caller can pass in an owned String which is dropped within from_str, the function can be written as follows without explicit lifetimes.

fn from_str(s: &impl AsRef<str>) -> Lexer<std::str::Chars> {
    Lexer {
        iter: s.as_ref().chars(),
        pos: 0,
    }
}

#5

Thanks this is what I want!


#6

If you wanted to allow borrowed or owned value to be passed in, then you’d likely want the following instead:

fn from_str<'a>(s: impl Into<&'a str>) -> Lexer<std::str::Chars<'a>> {
    Lexer {
        iter: s.into().chars(),
        pos: 0,
    }
}

This would let you pass an owned struct that holds a reference to a str slice (eg struct MyRef<'a>(&'a str)) or you can pass a reference to a str slice itself (such as from a borrowed String).


#7

Hmm this is probably more general. Maybe I should use this one.

EDIT: does this work (with the above definition)?

let s  = String::from("some text");
let lexer = from_str(&s);

#8

It should if you deref to &str (no deref coercions take place in generic call contexts): from_str(&*s) or from_str(s.as_ref())


#9

Is there a case for

impl<'a> From<&'a String> for &'a str

in the standard lib?