Why does this value not live long enough?

I'm trying to iterate over the lines in a file, but am having problems with the line values not living long enough for me to do anything with them:

use std::collections::HashMap;
use std::fs;
use std::io::BufRead;

const INPUT_PATH: &str = "day06.input.txt";

pub fn part1() {
    let input = fs::read(INPUT_PATH).unwrap();
    let mut hashmap = HashMap::new();

    for line in input.lines() {
        let parts = line.unwrap().split(')').collect::<Vec<_>>();
        let parent_name = parts[0];
        let node_name = parts[1];

        hashmap.insert(node_name, parent_name);
    }
}

(The input file is a newline-separated list of parent)node pairs.)

I get the error "temporary value dropped while borrowed" at line.unwrap(), with a suggestion to break it out into its own let statement. However, when I try this, it simply changes the error message to "`line` does not live long enough" on the let parts line, with no suggestion as to how to fix it

(Playground)

So the issue is, that the split function on String gives you iterator over references. After all, the parts variable has type Vec<&'lines str> - I used artificial 'lines lifetime to express, that those borrows cannot outlife line (which are borrowed from). Later you are inserting those borrows into hashmap, which clearly outlifes the lines variable.

Straight way to solve this problem is just change those borrows to owned strings with str::to_owned:

let parts = line.unwrap().split(')').map(str::to_owned).collect::<Vec<_>>();

However this would give you another error - you cannot this easly move things out of Vec (something have to exists for those indicies). The easy way is just Clone::clone() whatever you want to take out of Vec:

let parent_name = parts[0].clone()

but this isn't the best way - it comes out, that collecting into intermediate Vec is pointless here. Instead of collecting you may just leave it as iterator, change it to be mutable, ant take two first items from it with Iterator::next().

1 Like

Thanks for your reply! Unfortunately, I get the same errors when using .clone() and/or Iterator::next():

// "temporary value dropped while borrowed"
let parts = line.unwrap().split(')');
let parent_name = parts.next().clone();
let node_name = parts.next().clone();

// "`line` does not live long enough"
let line = line.unwrap();
let mut parts = line.split(')').clone(); // more cloning than needed, I'm sure
let parent_name = parts.next().clone();
let node_name = parts.next().clone();

(Playground)

You need to_owned instead of just clone, because rather than going from &String to String you are going from &str to String. (It looks like @hashedone forgot to actually add the to_owned; it probably should have looked like .map(str::to_owned) between the split(')') and the collect.)

You also need to either unwrap() or unwrap_or(...) the value you get from calling next, in case it returns None.

Also, bonus tip: std::fs::read_to_string is a quick way of reading in a whole file at once. Here's one way to write it:

let input = fs::read_to_string(INPUT_PATH).unwrap();
let mut hashmap = HashMap::new();

for line in input.lines() {
    let mut parts = line.split(')');
    let parent_name = parts.next().unwrap().to_owned();
    let node_name = parts.next().unwrap_or("default").to_owned(); // doesn't make much sense really but just for demonstration purposes

    hashmap.insert(node_name, parent_name);
}

That's done it! Here's my final code:

use std::collections::HashMap;
use std::fs;
use std::io::BufRead;

const INPUT_PATH: &str = "day06.input.txt";

pub fn main() {
    let input = fs::read(INPUT_PATH).unwrap();
    let mut hashmap = HashMap::new();

    for line in input.lines() {
        let line = line.unwrap();
        let mut parts = line.split(')').clone();
        let parent_name = parts.next().unwrap().to_owned();
        let node_name = parts.next().unwrap().to_owned();

        hashmap.insert(node_name, parent_name);
    }

    // do stuff with `hashmap`
}

Thanks so much to both of you for your help!

The clone in let mut parts = line.split(')').clone(); is actually obsolete here. It is very cheap clone, but it is good to avoid such things if its possible.

@trentj obvously you are right, I misstyped and forget to add to_owned in snippet - I edited my post for future readers.

1 Like