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?
'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.
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.
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,
}
}
1 Like
Thanks this is what I want!
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).
1 Like
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);
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())
1 Like
Is there a case for
impl<'a> From<&'a String> for &'a str
in the standard lib?