I know there are 1000 of these but I really don't understand how to fix this (I know that rust wants the variable to live past something) so if you could, could you not just give me the code (unless its like really simple) but try and explain how to fix it my self?
-
You should create a minimal failure case.
-
Are you okay with changing
dates: Vec<&str>
todates: Vec<String>
?
The vector name_out
goes out of scope and is dropped at the end of the if clause inside of the loop. The vector dates
then contains a &str
produced by name_out[0].as_str()
. A &str
is a borrowed string, the owner of name_out[0].as_str()
is still the vector name_out
.
You cannot keep that borrowed string inside of dates
after its owner is already dropped.
A possible simple soiution is to change dates
so that it owns its contents. So use Vec<String>
. You'll also need to insert a call to clone
in order to optain ownership of a copy of the string that you want to push. (And that contains
call needs a little tweaking, too.)
Modified code (but feel free to try it yourself first): Rust Playground
Maybe it also helps to correlate this explanation with the error message
Compiling playground v0.0.1 (/playground)
error[E0597]: `name_out` does not live long enough
--> src/main.rs:46:28
|
45 | if !dates.contains(&name_out[0].as_str()) {
| ----- borrow later used here
46 | dates.push(name_out[0].as_str());
| ^^^^^^^^ borrowed value does not live long enough
...
49 | }
| - `name_out` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error
The error message says that name_out
is being borrowed. It points out the point where it’s being dropped (i.e. the end of that if item.chars…
expression in line 49). And it also points to dates
.
Now the error message may be confusing due to the fact that it’s saying something about “later used here” pointing to a point in the code that appears to be “earlier” than everything else. To understand this better, note that while I argued that the mere presence of a borrowed value inside of the dates
array is problematic after the owner is dropped, it actually is slightly different with Vec
in that there’s actually only a problem with that if the vector is also still used after the owner of the contained reference is dropped.
Now, the code we’re talking about is a loop. The way to understand the compiler message this is
- (contents of) the vector
name_out
are still used after the vector is dropped. - Line
46
is where the borrow comes from - Line
49
is wherename_out
is dropped - Line
45
is wheredates
is used again in the next loop iteration
The precise connection between names_out
and dates
is not explained by the error message, you’ll have to (unfortunately) figure that out by yourself. What it does tell you is that
- somehow a borrow of (the vector in the variable)
name_out
ends up (directly or indirectly) in the variabledates
- It’s the borrow in line
46
that ends up indates
Fortunately the connection then is clear, because line 46
pushes a value into the vector 49
, and that value is a borrow of an item of name_out
.
Also, in case it isn’t clear why name_out[0].as_str()
borrows from name_out
even though there isn’t any &
-expression here, and the connection between name_out: Vec<String>
and the resulting &str
maybe isn’t immediately obvious.
Desugaring the method calls and indexing operation may help…
First, the types:
-
name_out
is aVec<String>
, soname_out[0]
is aString
- the method
String::as_str
takes an&self
argument and returns a&str
Now, desugaring:
- the
as_str
call inname_out[0].as_str()
introduces an implicit borrow (because it’s a&self
method so it expects&String
), soname_out[0].as_str()
desugars toString::as_str(&name_out[0])
and not e.g. something likeString::as_str(name_out[0])
orString::as_str(&mut name_out[0])
- the index expression
name_out[0]
is used for an immutable borrow, so it usesIndex::index
, notIndexMut::index_mut
. So we desugarname_out[0]
to*name_out.index(0)
. - The
Index::index
method/implementation onVec<String>
takes&self
, so we introduce another implicit borrow and desugarname_out.index(0)
toIndex::index(&name_out, 0)
Combining all the desugarings, we get
name_out[0].as_str()
String::as_str(&name_out[0])
String::as_str(&*name_out.index(0))
String::as_str(&*Index::index(&name_out, 0))
In particular, we do now clearly see that there’s a borrow of name_out
being created (you see the &
).
The &*
de-references and re-references again, the two together are redundant here because Index::index(&name_out, 0)
already returns a shared reference &String
, so the above is the same as String::as_str(Index::index(&name_out, 0))
, without the “&*
”
Both String::as_str
and Vec<String>
’s Index::index
implementation are methods that transform borrows. They have types fn (&Vec<String>, usize) -> &String
and fn(&String) -> &str
. They take a borrow as an argument and return a different borrow that still borrows from the same owner. That’s ultimately the reason why
- the expression
name_out[0].as_str()
borrowsname_out
in the first place (because it has&name_out
in its desugaring), and why - the
&str
that it evaluates to is a borrow for which the compiler still considersname_out
to be its corresponding owner (becauseas_str
and indexing transforms the borrows, which doesn’t change the owner).
Not really on topic, but I've made a regex version of your program, mostly because quickly (measured in dev time) parsing log files is (sadly?) something I've had to do too much in my life. It uses some pretty idiomatic Rust, though it could still be improved.
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.