3

Here is segment I've a problem with, basically just pushing a &str to Vec<&str> in the loop

fn main() {
    let mut accounting = vec!["Alice", "Ben"];
    
    loop {
        let mut add_input = String::from("");

        io::stdin()
            .read_line(&mut add_input)
            .expect("Failed to read line");

        let add_vec: Vec<&str> = add_input.trim()[..].split_whitespace().collect();

        if add_vec.len() < 1 {
            println!("Incorrect input, try again");
            continue;
        }

        let person = add_vec[0];
        accounting.push(person);
    }
}

let add_vec: Vec<&str> = add_input.trim()[..].split_whitespace().collect(); - here's where I get "borrowed value does not live long enough".

I was able to make my code work by changing target vector signature from Vec<&str> to Vec<String> and pushing not a &str but &str.to_string()

I understand that pushing a &str bacibally making it invalid to for the scope that .push was called in, but while that is happening right at the end of the loop - why is that a problem?

The error give is:

$ rustc main.rs
error[E0597]: `add_input` does not live long enough
  --> main.rs:14:34
   |
14 |         let add_vec: Vec<&str> = add_input.trim()[..].split_whitespace().collect();
   |                                  ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
22 |         accounting.push(person);
   |         ----------------------- borrow later used here
23 |     }
   |     - `add_input` dropped here while still borrowed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
Luuk
  • 12,245
  • 5
  • 22
  • 33
Daniel
  • 33
  • 4
  • Does this answer your question? [error \[E0716\]: temporary value dropped while borrowed (rust)](https://stackoverflow.com/questions/71626083/error-e0716-temporary-value-dropped-while-borrowed-rust) – Luuk Mar 04 '23 at 09:08
  • Unfortunately no, I mean, I get the main idea there, but it's not quite clear how to implement the fix in my own code and it's still not clear to me why borrowing before (on line 59) interfere with dropping later (on line 68). – Daniel Mar 04 '23 at 12:26
  • Because stackoverflow does not show line number, you might want to include the code that is (or was) on line 59 and 68, because currently in the provided link to github i do see: `68 },` – Luuk Mar 04 '23 at 12:35
  • Sorry, I wasn't clear. At line 68 there is and end of the match arm. But I've taken anothe look at the question that you've linked and it indeed does 90% answer my question. I was able to make my code work by changing vector I'm trying to push to from Vec<&str> to Vec. – Daniel Mar 04 '23 at 12:47
  • I've simplified the example to contain only the essentials. I do know now how to make it work, but if you will have some time to clarify - that would be great. My understanding is, that when we use a Vec of &str, after we push a new element to that vector, it becomes invalid later in the same scope. But that push happens exactly at the end of the scope, so why is that a problem? – Daniel Mar 04 '23 at 13:05
  • I added, the formatted, text of the error. – Luuk Mar 04 '23 at 13:38
  • 3
    `add_vec` is a `vec` containing references to slices of strings that live on the stack. Those only live between the brackets declared as a loop statement. You then want to add these references to string slices to a `vec` that is created outside of the loop. The problem is that when the loop exists, the string slices will get dropped, and the `vec` outside will have dangling references. That is not allowed. By changing it to `strings` instead, they are now stored on the heap, and will not get dropped when we exit the loop. – Toerktumlare Mar 04 '23 at 14:20
  • That answers my questions fully. Can I mark a comment as an answer? – Daniel Mar 04 '23 at 15:11
  • 1
    @Toerktumlare, `add_vec` references into data that is allocated on heap and not on stack. But the `String` container that manages this data does live on stack, which is dropped (and it inturn then frees the heap allocated data) once it goes out of scope. – vikram2784 Mar 05 '23 at 14:25
  • 1
    And this is the reason i never answer any rust questions because the rust community always feels the need to nitpick on others answer. Feel free to edit or answer yourself with proper references to your claims and be helping – Toerktumlare Mar 05 '23 at 14:30
  • 2
    I never said your answer is incorrect. I just added a bit more detail to it. Sorry if that came as rude to you but was never my intention. https://doc.rust-lang.org/std/string/struct.String.html#impl-From%3C%26str%3E-for-String – vikram2784 Mar 05 '23 at 15:08

1 Answers1

2

first you create i vec in you main method. It will have the lifetime of the brackets surrounding the `main function.

fn main {
    // accounting has a lifetime between these 2 brackets.
    let mut accounting = vec!["Alice", "Ben"];
}

Later you create a new vec containing references inside the loop.

fn main {
    loop {
        // add_vec has a lifetime between the loop brackets
        let add_vec: Vec<&str> = add_input.trim()[..].split_whitespace().collect();
    }
}

That vec has lifetime between the loop bracket. Since that vec contains &str it only contains references to slices of strings that are most likely stored on the stack that is accessible between the loop brackets.

Later in your code you want to add this vec of &str to the outer vec. Its here we will get a problem. The problem is that when we exit the loop the str will get dropped, and what is left will be a vec with a lot of references that are not referencing something.

Hence your error:

borrowed value does not live long enough

The borrowed value (&str) between the loop brackets wont live long enough to be stored outside the brackets.

fn main {
    loop {
        // If you create something on the stack between these brackets

    } <--- it will get dropped here
}

So one solution here is if you want to design your code this way is that you instead from using &str switch to using String which will allocate memory on the heap for the string instead. This means the string won't get dropped when we exit the loop brackets.

But remember by allocating memory like that your program will consume more memory which is not always feasible (if you are in a memory constrained platform like for instance embedded)

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54