Borrowed value does not live log enough

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?

code

  1. You should create a minimal failure case.

  2. Are you okay with changing dates: Vec<&str> to dates: 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

1 Like

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 where name_out is dropped
  • Line 45 is where dates 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 variable dates
  • It’s the borrow in line 46 that ends up in dates

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 a Vec<String>, so name_out[0] is a String
  • the method String::as_str takes an &self argument and returns a &str

Now, desugaring:

  • the as_str call in name_out[0].as_str() introduces an implicit borrow (because it’s a &self method so it expects &String), so name_out[0].as_str() desugars to String::as_str(&name_out[0]) and not e.g. something like String::as_str(name_out[0]) or String::as_str(&mut name_out[0])
  • the index expression name_out[0] is used for an immutable borrow, so it uses Index::index, not IndexMut::index_mut. So we desugar name_out[0] to *name_out.index(0).
  • The Index::index method/implementation on Vec<String> takes &self, so we introduce another implicit borrow and desugar name_out.index(0) to Index::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() borrows name_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 considers name_out to be its corresponding owner (because as_str and indexing transforms the borrows, which doesn’t change the owner).
2 Likes

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.