Singh
Singh

Reputation: 301

"Borrowed Value Does Not Live Long Enough" when pushing into a vector

I am trying a daily programmer problem to shuffle a list of arguments and output them.

I'm not sure if this is the correct approach but it sounded like a good idea: remove the element from the args vector so it doesn't get repeated, and insert it into the result vector.

extern crate rand; // 0.7.3

use std::io;
use std::cmp::Ordering;
use std::env;
use rand::Rng;

fn main() {
    let mut args: Vec<_> = env::args().collect();
    let mut result: Vec<_> = Vec::with_capacity(args.capacity());

    if args.len() > 1 {
        println!("There are(is) {} argument(s)", args.len() - 1)
    }

    for x in args.iter().skip(1) {
        let mut n = rand::thread_rng().gen_range(1, args.len());
        result.push(&args.swap_remove(n));
    }

    for y in result.iter() {
        println!("{}", y);
    }
}

I get the error:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:18:22
   |
18 |         result.push(&args.swap_remove(n));
   |                      ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                      |
   |                      creates a temporary which is freed while still in use
...
21 |     for y in result.iter() {
   |              ------ borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

Older compilers said:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:18:42
   |
18 |         result.push(&args.swap_remove(n));
   |                      ------------------- ^ temporary value dropped here while still borrowed
   |                      |
   |                      temporary value created here
...
24 | }
   | - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime

Upvotes: 2

Views: 1996

Answers (1)

Shepmaster
Shepmaster

Reputation: 431619

Let's start with a smaller example. This is called an Minimal, Reproducible Example , and is very valuable for both you as a programmer and for us to answer your question. Additionally, it can run on the Rust Playground, which is convenient.

fn main() {
    let mut args = vec!["a".to_string()];
    let mut result = vec![];

    for _ in args.iter() {
        let n = args.len() - 1; // Pretend this is a random index
        result.push(&args.swap_remove(n));
    }

    for y in result.iter() {
        println!("{}", y);
    }
}

The problem arises because when you call swap_remove, the item is moved out of the vector and given to you - the ownership is transferred. You then take a reference to the item and try to store that reference in the result vector. The problem is that the item is dropped after the loop iteration has ended because nothing owns it. If you were allowed to take that reference, it would be a dangling reference, one that points to invalid memory. Using that reference could cause a crash, so Rust prevents it.

The immediate fix is to not take a reference, but instead transfer ownership from one vector to the other. Something like:

for _ in args.iter() {
    let n = args.len() - 1; // Pretend this is a random index
    result.push(args.swap_remove(n));
}

The problem with this is that you will get

error[E0502]: cannot borrow `args` as mutable because it is also borrowed as immutable
 --> src/main.rs:7:21
  |
5 |     for _ in args.iter() {
  |              -----------
  |              |
  |              immutable borrow occurs here
  |              immutable borrow later used here
6 |         let n = args.len() - 1;
7 |         result.push(args.swap_remove(n));
  |                     ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

See the args.iter? That creates an iterator that refers to the vector. If you changed the vector, then the iterator would become invalid, and allow access to an item that may not be there, another potential crash that Rust prevents.

I'm not making any claim that this is a good way to do it, but one solution would be to iterate while there are still items:

while !args.is_empty() {
    let n = args.len() - 1; // Pretend this is a random index
    result.push(args.swap_remove(n));
}

I'd solve the overall problem by using shuffle:

use rand::seq::SliceRandom; // 0.8.3
use std::env; 

fn main() {
    let mut args: Vec<_> = env::args().skip(1).collect();

    args.shuffle(&mut rand::thread_rng());

    for y in &args {
        println!("{}", y);
    }
}

Upvotes: 10

Related Questions