0
votes

I am banging my head trying to figure out Rust's borrowing/lifetime/ownership properties. Namely, when using a buffered reader, and attempting to split a line. The code

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = File::open("foo.txt").expect("file not found");
    let f = BufReader::new(f);

    for line in f.lines() {
        let split: Vec<&str> = {
            let ln: String = line.unwrap();
            ln.split(' ').collect()
        };
    }
}

or any variation of (with or without specifying variable type, futile attempts to make it mutable, etc) results in:

'ln' does not live long enough; borrowed value must only be valid for the static lifetime...

yet trying to maybe fake an extended lifetime and grab some data out of the line via a slice

let nm = line;
name = &line[..];

or even just trying to operate the split() on the unmodified line variable results in:

cannot index into a value of type 'std::result::Result<std::string::String, std::io::Error>'

"borrowed value does not live long enough" seems to blame the wrong thing suggests that the lifetime lasts long enough to put each word into its own string, but modifying my original code on the Playground to include that nested for loop still results in a

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:11:18
   |
11 |         for w in line.unwrap().split_whitespace() {
   |                  ^^^^^^^^^^^^^ temporary value does not live long enough
...
14 |         }
   |         - temporary value dropped here while still borrowed
15 |     }
   |     - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime

in reference to the line.unwrap()

Ultimately, what am I misunderstanding here about Rust's lifetime or borrowing properties?

1
The error you have here is not the error that your code creates. Please review how to create a minimal reproducible example that accurately recreates your problem. Namely, the error has nothing about 'static.Shepmaster
Or even better: “borrowed value does not live long enough” seems to blame the wrong thing. If not, please edit your question to explain the difference.Shepmaster
Perhaps you could edit your question to explain what you don't understand about the error message that you get from the compiler? For whatever reason, you have been only pasting one line of each of the error messages; I've pasted the entire error in, which seems pretty lucid and descriptive. Also, you are asking about an error that mentions the 'static lifetime, but none of the code you've provided has that error. We cannot help you fix an error that we cannot see.Shepmaster

1 Answers

1
votes

The error your original code gives when compiled is:

error[E0597]: `ln` does not live long enough
  --> src/main.rs:11:13
   |
11 |             ln.split(' ').collect()
   |             ^^ borrowed value does not live long enough
12 |         };
   |         - `ln` dropped here while still borrowed
13 |     }
   |     - borrowed value needs to live until here

error: aborting due to previous error

As per @shepmasters comments it is a good idea to provide the full error when posting a question.

Anyway, it highlights the problem:

let split: Vec<&str> = {
    let ln: String = line.unwrap();
    ln.split(' ').collect()
};

You are creating a Vec containing references to str slices; slices don't own the data that they are sliced from, they are effectively pointers into data which has to be owned by another variable. Therefore the variable that they are sliced from have to outlive the slices.

Inside the expression you are using to initialise the Vec, you create a String containing the line of text that you are processing. The scope of this string is the variable ln is the initialisation expression - it will be dropped as soon as you leave that scope.

Then you split the string, which returns an iterator to string slices, one per substring. Remember though, the iterator is returning slices, which are pointers to the substrings in the String ln. Those slices are not allowed to outlive ln itself.

Hopefully you can see the problem now. As soon as you exit the initialisation expression, ln is dropped, but the Vec would still contain the str slices. What are they pointing to?

The fix is very simple. Why declare ln inside that block? In fact why have a block there at all? This works:

for line in f.lines() {
    let ln: String = line.unwrap();
    let split: Vec<&str> = ln.split(' ').collect();
    // Now do something with split
}