95

I am playing with Rust, and I'm trying to access the first command line argument with this code:

use std::env;

fn main() {
    let args: Vec<_> = env::args().collect();
    let dir = args[1];
}

And I get this error:

error[E0507]: cannot move out of indexed content
 --> src/main.rs:5:15
  |
5 |     let dir = args[1];
  |         ---   ^^^^^^^ cannot move out of indexed content
  |         |
  |         hint: to prevent move, use `ref dir` or `ref mut dir`

Or in later versions of Rust:

error[E0507]: cannot move out of index of `std::vec::Vec<std::string::String>`
 --> src/main.rs:5:15
  |
5 |     let dir = args[1];
  |               ^^^^^^^
  |               |
  |               move occurs because value has type `std::string::String`, which does not implement the `Copy` trait
  |               help: consider borrowing here: `&args[1]`

If I change it to let ref dir, it compiles, but I don't grok what's going on. Could someone explain what "indexed content" means?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Amandasaurus
  • 58,203
  • 71
  • 188
  • 248

2 Answers2

102

When you use an index operator ([]) you get the actual object at index location. You do not get a reference, pointer or copy. Since you try to bind that object with a let binding, Rust immediately tries to move (or copy, if the Copy trait is implemented).

In your example, env::args() is an iterator of Strings which is then collected into a Vec<String>. This is an owned vector of owned strings, and owned strings are not automatically copyable.

You can use a let ref binding, but the more idiomatic alternative is to take a reference to the indexed object (note the & symbol):

use std::env;

fn main() {
    let args: Vec<_> = env::args().collect();
    let ref dir = &args[1];
    //            ^
}

Implicitly moving out of a Vec is not allowed as it would leave it in an invalid state — one element is moved out, the others are not. If you have a mutable Vec, you can use a method like Vec::remove to take a single value out:

use std::env;

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

See also:


For your particular problem, you can also just use Iterator::nth:

use std::env;

fn main() {
    let dir = env::args().nth(1).expect("Missing argument");
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
oli_obk
  • 28,729
  • 6
  • 82
  • 98
  • 7
    What if I own the array, and want to take ownership of a single value in the array (and consume ownership of the array)? – Thayne Feb 04 '17 at 04:50
  • 4
    if it's a `Vec` you can use the `remove` method, otherwise you can `mem::replace` the value with a dummy value. – oli_obk Feb 06 '17 at 10:44
  • `vec.into_iter().nth(1).expect("Missing element")` works – Tomáš Dvořák Mar 30 '18 at 19:21
  • A remark: the Index trait looks like it should return a reference, but as this answer states, it does not. See also https://stackoverflow.com/questions/27879161/what-is-the-return-type-of-the-indexing-operation – nnnmmm Jul 24 '18 at 11:05
  • "Rust immidiately tries to move" - why does it try to move instead of passing the ownership of this String? – Anatoly Bugakov May 14 '22 at 20:09
5

The accepted answer has already given the solution. I would like to explain this problem on a semantic level as a complement.

The rule is: A borrowed value can't be moved out. See this: E0507

[] operator came from the Index trait, whose function signature is:

fn index(&self, index: I) -> &<Vec<T, A> as Index<I>>::Output

As you can see, it return a reference, not own the value. Moving it out break the rule mentioned above.

tshepang
  • 12,111
  • 21
  • 91
  • 136
wahaha
  • 311
  • 4
  • 3