Borrowed value does not live long enough, reading file

Trying to complete the advent of code (I know it's a little bit late) I have the following problem when I try to read the input file.

I created a new module with some utility methods that I can use to solve every excercise. This file contains just functions to help read the file content and organizing the output:

use std::fs;

pub fn read_file(filename: &str) -> String {
    fs::read_to_string(filename)
        .expect("Something went wrong reading the file.")
}

pub fn get_file_content_lines(filename: &str) -> Vec<&str> {
    let content = read_file(filename);

    content.lines().collect()
}

pub fn read_and_convert_to_int(file_content: Vec<&str>) -> Vec<i32> {
    let mut numbers: Vec<i32> = vec![];

    for line in file_content {
        numbers.push(line.parse::<i32>().unwrap());
    }

    numbers
}

With the following error:

error[E0597]: `content` does not live long enough
     --> src/utility.rs:13:5
   |
13 |     content.lines().collect()
   |     ^^^^^^^ borrowed value does not live long enough
14 | }
   | - borrowed value only lives until here

Well, content is a String, and lines returns references into that string, that you're trying to collect into a Vec and return them. But at the end of the function, the local variable content is dropped, so the references become dangling.

If you're only interested in a quick fix, you can return a Vec<String> from get_file_content, and them make the last line this:

content.lines().map(String::from).collect()

okay, it works thank you.

Since I'm learning Rust, what would it be the 'slower' fix? I could I fix it by returning a Vec<&str>?

To have a Vec<&str>, you must have a String which live at least as long as this Vec. That means, it must be created before the function containing collect() call is called.

Okay, I've done it.

Thank you.

Yes, but it needs more than that. Did you read the book yet, the chapter on ownership and references? If so, the following outlines what you could do: Find another owner for content, maybe just the main function. Then pass a reference to it into get_file_content_lines (which would then probably be called just get_lines or something), which then could return a Vec<&str> since the references then would not have to refer to a local variable. This has the advantage of not having to copy each line, at the expense of a more elaborate setup. But it's a good learning step for sure to do this, I suggest you try and come back if you have trouble with it.

1 Like

I just created a variable containing the String before the call to read_file:

pub fn get_file_content_lines(filename: &str) -> Vec<&str> {
    let mut content = String::new();
    content = read_file(filename);

    content.lines().collect()
}

This solution works, and in this case I can keep all together.

Could you explain a bit more? I wasn't able to get this code to work (playground), since content now still is too short-lived.

Yesterday I installed rust on this pc and I don't know why it installed and older version of rust. I just updated it and... it doesn't works anymore.

That's basically the same as before and should never have worked, for the same reasons.

Just checked on rust 1.26 (the first version to have fs::read_to_string, AFAIK) - the same error. Anyway, we've already told where is the problem - feel free to ask if something is not clear yet.