Temporary Value Dropped While Borrowed Using into_iter

This is the last example from the [https://doc.rust-lang.org/stable/rust-by-example/flow_control/for.html] page:

fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        }
    }

    println!("names: {:?}", names);
}

It outputs this:

names: ["Hello", "Hello", "There is a rustacean among us!"]

I was attempting to modify the example so that it outputs this:

names: ["Hello, Bob", "Hello, Frank", "There is a rustacean among us!"]

Here is an example of one of many of my unsuccessful attempts:

fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => ("Hello, ".to_string() + *name).as_mut_str(),
        }
    }

    println!("names: {:?}", names);
}

This generates this error message:

error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:7:18
  |
5 |           *name = match name {
  |  _________________-
6 | |             &mut "Ferris" => "There is a rustacean among us!",
7 | |             _ => ("Hello, ".to_string() + *name).as_mut_str(),
  | |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^            - temporary value is freed at the end of this statement
  | |                  |
  | |                  creates a temporary which is freed while still in use
8 | |         }
  | |_________- borrow later used here
  |
  = note: consider using a `let` binding to create a longer lived value

Any suggestions as to how to fix this would be appreciated.

If you wrap the entirety of your code (including the fn main() { block) in code-quotes, and put your output in code-quotes too, your post will be more legible for others.

As for your question: references like &str have to outlive the memory they point to. Literals like "Ferris" and "Hello" have a type, including lifetime, of &'static str. 'static is the lifetime. It's a special lifetime that means "the underlying memory will be around for the entire runtime of the program". These references can live for the entire length of your program because the literals are built into the executable.

But when you create a String at runtime, it gets allocated memory during runtime and has a lifetime shorter than the whole program -- shorter than 'static. Any reference to the String has to have a lifetime less than the String itself. Any &str you create from the String has to go away before the String goes away.

So, to your program. In this portion:

("Hello, ".to_string() + *name)

You're creating a new String, then extending it with + *name. No problems so far. But note that it is a temporary value -- you're not storing it in a variable or anything. Then you create a reference to it, because your Vec of names stores references:

("Hello, ".to_string() + *name).as_mut_str()

And this is where there's a problem. You're trying to store this reference in the Vec, which lasts beyond the loop -- you print it out later. But, as the error message says, your temporary String goes away at the end if the statement. You can't have a reference that lives longer than what it points to, so this is an error.

It wasn't a problem in the original, because there was no Strings created; everything was a literal that could last the entire program.

So, how to fix it? The most straightforward way is to make your vector a Vec<String> instead of a Vec<&str>. Then the data is stored within the Vec, you can expand the Strings it holds, and so on.

Below is a version where I have done that (or check it out on the Playground):

fn main() {
    let mut names = vec!["Bob".to_string(), "Frank".to_string(), "Ferris".to_string()];

    for name in names.iter_mut() {
        *name = match name.as_str() {
            "Ferris" => "There is a rustacean among us!".to_string(),
            _ => "Hello, ".to_string() + name,
        }
    }

    println!("names: {:?}", names);
}

The problem was one of ownership and borrowing, which is a topic that tends to have a steep learning curve for people starting out in Rust. Here's the section of the book that covers the topic. The examples it works with covers Strings and &strs as well.

3 Likes

Please read Forum Code Formatting and Syntax Highlighting and edit your initial post by using the pencil-shaped edit button under that post.

Many readers of this forum will ignore code snippets, compiler error reports, etc that do not follow the posting guidelines for new contributors that are pinned to the top of this forum. Even those who do respond, such as @quinedot, may feel that the lack of following the forum posting guidelines is disrespectful of their time. Thanks. :clap:

Thank you very much for the rapid response and the very thorough explanation. It was very helpful.

Currently you are using a vector of type &str, but such a type does not own its memory. There must be some other variable that owns the memory, and this other variable may not be modified while you are holding a reference to it.

To fix this, change the vector type to String, which does own its string data.

fn main() {
    let mut names = vec!["Bob".to_string(), "Frank".to_string(), "Ferris".to_string()];

    for name in names.iter_mut() {
        *name = match name.as_str() {
            "Ferris" => "There is a rustacean among us!".to_string(),
            _ => ("Hello, ".to_string() + &name),
        }
    }

    println!("names: {:?}", names);
}

Note that string constants are short-hand for creating a reference to an immutable global containing the string data.