error[E0507]: cannot move out of indexed content


#1

I have the following code, which I am trying to set a variable to an element of a Vector.

fn main(){
    let mut a = vec!["hello".to_string(), "world".to_string()];
    let mut index = -1;
    let mut b = "".to_string();

    for i in a.clone() {
        index = index + 1;
        b = a[index as usize];
    }
}

However, it is erring the following message:

error[E0507]: cannot move out of indexed content
 --> test.rs:8:13
  |
8 |         b = a[index as usize];
  |             ^^^^^^^^^^^^^^^^^ cannot move out of indexed content

error: aborting due to previous error(s)

Why is it doing this? There’s no way the index can be negative, as the first thing the loop does it increase it by one. That is the only reason I can think this error message would appear, but I’m probably wrong.


#2

In order to assign a string to b, you have to move it or copy it from where it was. You can’t move it out of a, because that would be unsafe – the string owned by b would then be pointing to somewhere inside a. So, your only option is to copy it out. Because String is non-Copy, you would have to clone it:

b = a[index as usize].clone();

By the way, a more idiomatic way to write that loop would be

for b in a {
    // do whatever with b
}

Or, if you still want access to the index,

for i in 0..a.len() {
    b = a[i].clone();
}

#3

You can do this in multiple ways, just not the “C” way :slight_smile:

What happens here technically is that for owned values Rust needs to know when to free them. If it has a Vec full of Strings, then at the end of the function (vec’s lifetime) it will free every element of that vector. However, if you start copying and removing strings at will, Rust will lose track of which strings are in the vector, and which were moved, so it won’t know how to free the entire vector. It doesn’t do clever tracking per element unless you tell it to (and you can tell it with e.g. Option<String> elements and .take()).

You can do:

for s in a {
 b = s;
} 

This “consumes” the vec a, and moves elements out of it one by one.

Or you can do:

for i in 0..a.len() {
 b = a.pop();
} 

which shortens the vector.

or

for i in 0..a.len() {
 std::mem::swap(&mut b, &mut a[i]);
} 

which swaps the elements, so the content is moved, but the vector doesn’t get any “holes”.

Or if you don’t need your own editable copy of the string in b, then do:

for s in a.iter() {
 b = s;
} 

which makes b a read-only reference to an existing element in the vector.

There’s also vec.remove(index) and vec.drain(range) if you want to move specific elements out of a vector. Your code looks abstract, so it’s hard to recommend right approach without some specific goal to achieve.