Rust can't borrow a newly declared variable?

Viewed 83

This code is suppose to be a simple while loop. Until I enter the word exit it should keep taking in a list of names and print out the vector. I am using this to test my understanding of borrowing.

use std::io;

fn main() { 

    let mut name = String::new();
    let mut vec = Vec::new();

    while name.ne("exit") {
        print!("Enter a name"); 
        io::stdin().read_line(&mut name).expect("failed to readline");
    
        print!("You entered {}", name); 

        vec.push(name); 
        println!("length of vector is {}", vec.len());

        for i in &vec {
            println!("name in vector is {}", i);
        }  
    }

}

This code will not compile due to the following error:

~/rust/vectors/src$ cargo build
   Compiling vectors v0.1.0 (rust/vectors)
error[E0382]: borrow of moved value: `name`
  --> src/main.rs:8:11
   |
5  |     let mut name = String::new();
   |         -------- move occurs because `name` has type `String`, which does not implement the `Copy` trait
...
8  |     while name.ne("exit") {
   |           ^^^^^^^^^^^^^^^ value borrowed here after move
...
14 |         vec.push(name); 
   |                  ---- value moved here, in previous iteration of loop

For more information about this error, try `rustc --explain E0382`.
error: could not compile `vectors` due to previous error

I thought it was a mutable reference that I have to borrow so I changed the while loop to this: while &mut name.ne("exit") but that just gave me this error:

   Compiling vectors v0.1.0 (rust/vectors)
error[E0308]: mismatched types
 --> src/main.rs:8:11
  |
8 |     while &mut name.ne("exit") {
  |           ^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `&mut bool`
  |
help: consider removing the borrow
  |
8 -     while &mut name.ne("exit") {
8 +     while name.ne("exit") {
  | 

For more information about this error, try `rustc --explain E0308`.
error: could not compile `vectors` due to previous error
1 Answers

The move is not within ne(), and thus borrowing it there will not solve the problem (also, to borrow it you need to do (&mut name).ne("exit"); &mut name.ne("exit") will borrow the result of ne() mutably). ne() borrows the value with a shared reference.

The move, as the compiler tells you, is within vec.push(name). Here we move name, and any further attempt to access it is an error. But in the next round of the loop we'll try to do exactly that to compare it with "exit"!

There are generally two solutions to that:

  1. Clone the string and push the cloned string to the vector, leaving the original unmodified.
vec.push(name.clone());
  1. Instead of checking name directly, check the last value in the vector. We need to be careful to not assume there is such value, because the vector is empty in the beginning of the first iteration:
let mut vec = Vec::<String>::new();

while vec.last().map(|v| &**v) != Some("exit") {
    let mut name = String::new();

    print!("Enter a name");
    io::stdin()
        .read_line(&mut name)
        .expect("failed to readline");

    print!("You entered {}", name);

    vec.push(name);
    println!("length of vector is {}", vec.len());

    for i in &vec {
        println!("name in vector is {}", i);
    }
}

But in your case, it is more likely that you don't want to push "exit" into the Vec at all. So, it is better to use loop with break:

let mut vec = Vec::<String>::new();
loop {
    let mut name = String::new();
    print!("Enter a name");
    io::stdin()
        .read_line(&mut name)
        .expect("failed to readline");

    if name == "exit" {
        break;
    }

    print!("You entered {}", name);

    vec.push(name);
    println!("length of vector is {}", vec.len());

    for i in &vec {
        println!("name in vector is {}", i);
    }
}
Related