Borrowed value does not live long enough


#1

Can you help me to understand why is this not compiling?
I’m in the process of learning rust and I’ve been banging my head with this peace of code for an hour.
AFAIK what I’m doing here is to get the lines — an Option that I unwrap in order to access its values (will panic if empty) then split by white space.

Then, I’m checking the first result of the iterator and print the result. The following code does not compile

fn reduce() {
     let stdin = io::stdin();
     for line in stdin.lock().lines() {

         let parts = line.unwrap().split_whitespace();

         match parts.next() {
             Some(key) => {
                 println!("{}", key);
             }
             None => break,
         }
     }
 }

In the other hand, it works if I loop over the cols

fn reduce() {
       let stdin = io::stdin();
       for line in stdin.lock().lines() {
         for col in line.unwrap().split_whitespace() {
             println!("> {}", col);
         }
      }
}

Why match is different?


#2

Oh you should check out the error message the nightly reports (thanks to transition to MIR I guess):

error: borrowed value does not live long enough
  --> <anon>:8:26
   |
8  |          let mut parts = line.unwrap().split_whitespace();
   |                          ^^^^^^^^^^^^^                   - temporary value only lives until here
   |                          |
   |                          temporary value created here
...
16 |      }
   |      - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime

SplitWhitespace returned by split_whitespace borrows its self argument (must be outlived by line) but line is being thrown away in that line because unwrap consumes its argument thus the string backing parts doesn’t live long enough [unwrap consumes Result<String> and returns String, which is not stored anywhere]. You can fix it by binding the string:

         let line = line.unwrap();
         let mut parts = line.split_whitespace();

Alternatively, avoid consuming line by first calling as_ref (‘converting’ Result<String> to Result<&String>):

         let mut parts = line.as_ref().unwrap().split_whitespace();

#3

Thank you very much!
Code compiles and works as expected with your changes.
I still do not get why

 let line = line.unwrap();
 let mut parts = line.split_whitespace();

and

let mut parts = line.unwrap().split_whitespace();

Are not equivalent. At the end we are just chaining, aren’t we?


#4

The parts contain borrowed strings that point back into the line. Therefore Rust ensures that line lives at least as long as the parts.

However, in the second version the result of unwrap() is a temporary, and the temporary lives only until the end of the statement. Rust does not automatically extend the lifetime of temporaries for you (that would create a host of other problems with borrows living longer than you want them to).


#5

I have seen many people asking similar questions. It makes complete sense once you understand why it does not work. However, most people I know fail to write it correctly the first time.

The reason is (in my opinion) that is counter intuitive. The author’s intention writing line.unwrap().split_whitespace() is clear: he/she pretends to consume self and use the result.

I wonder if there is any way to make this simpler (without automatically extending the lifetime of temporaries).


#6

I wrote a rather long explanation about a similar case here:

Lifetimes interact with bindings, and as parts points to line, line needs to be bound.

I consider that a feature.


#7

Thank you very much!
Now is it clear to me why and how this is working