Reputation: 2173
Why does this happen in this certain scenario? I'm new to Rust, read the 2nd edition book, but.. well, yeah, here I am. :)
fn main() {
Xyz::new(&"whatever=123");
}
pub struct Xyz<'a> {
x: &'a str
}
impl<'a> Xyz<'a> {
pub fn new(query: &str) -> Result<Xyz<'a>, &'a str> {
let mut qkey: String = String::new();
let mut qval: String = String::new();
let mut is_key = true;
for (i, c) in query.chars().enumerate().skip(1) {
if c == '=' {
is_key = false;
} else if c == '&' {
is_key = true;
} else if is_key {
qkey.push(c);
} else {
qval.push(c);
}
if c == '&' || i == query.len() - 1 {
match qkey.as_ref() {
"whatever" => {
let _whatever = Xyz::some_func(&mut qval)?;
}
_ => (),
}
qkey.clear();
qval.clear();
}
}
Ok(Xyz {
x: &""
})
}
fn some_func(_val: &mut String) -> Result<bool, &str> {
Ok(true)
}
}
Error:
error[E0597]: `qval` does not live long enough
--> src/main.rs:29:61
|
29 | let _whatever = Xyz::some_func(&mut qval)?;
| ^^^^ borrowed value does not live long enough
...
41 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 9:1...
--> src/main.rs:9:1
|
9 | impl<'a> Xyz<'a> {
| ^^^^^^^^^^^^^^^^
I don't understand why the actual error happens. I know the note should help me understand the problem, but it doesn't.
I don't need qval
outside of this function, so why do I have to still keep it? Is there a conceptual error I made?
Upvotes: 0
Views: 2608
Reputation: 161637
A trimmed down reproduction of your issue is
pub struct Xyz<'a> {
x: &'a str
}
impl<'a> Xyz<'a> {
pub fn new(_query: &str) -> Result<Xyz<'a>, &'a str> {
let qval: String = String::new();
Err(&qval)
}
}
with the error
error[E0597]: `qval` does not live long enough
--> src/main.rs:13:18
|
13 | Err(&qval)
| ^^^^ borrowed value does not live long enough
14 | }
| - borrowed value only lives until here
To understand this, I'd recommend taking a step back and thinking about what exactly your function is doing. You create a String
, and then take a reference to it, and then return that reference.
This is would be a classic case of use-after-free. From the standpoint of the language, the string no longer exists, so returning a reference to it does not make sense.
Returning &str
could make sense, but only if you can guarantee that the &str
will only reference data that is still in scope, even after the function has returned. For instance, it would be valid to do Err(&query)
(or any subsection of query
) if you adjust the function to be fn new(query: &'a str) ->
, since then it is it is known that the return value lives as long as the input string.
It is hard to tell from your example if that would be acceptable for usecase.
The simplest would indeed be to return a String
(or Result<Xyz<'a>, String>>
in your case, possibly with a String
in the field for Xyz
too). You could also consider returning something like a Cow which is sometimes a subsection of query
or a static string, and sometimes a String
.
As a side-note, I'll also add that Xyz::new(&"whatever=123");
does not need the &
since it is already a reference. You can do
Xyz::new("whatever=123");
just fine. Same for &""
.
My confusion was that - in the not stripped down version - both function (
new
andsome_func
returned a global&'static str
asErr
, so I thought the compiler would know that the string would always exist.
This may be the core of the misunderstanding. When you declare
fn some_func(_val: &mut String) -> Result<bool, &str> {
the &str
is not 'static
. Rust's lifetime elision logic means that this declaration will assume that the reference in the function argument and the reference in the return value have the same lifetime, meaning that what you've declared is
fn some_func<'b>(_val: &'b mut String) -> Result<bool, &'b str> {
which triggers the error you are seeing. If you want it to be static, you'll need to say so, as
fn some_func(_val: &mut String) -> Result<bool, &'static str> {
which would avoid the error, and require that some_func
always returns a 'static
string.
Upvotes: 4