2

The I/O project example in the Rust book suggests cloning the command line args:

fn main() {
    let args: Vec<String> = env::args().collect();
    let config = parse_config(&args);
    // ...
}

struct Config {
    query: String,
    filename: String,
}

fn parse_config(args: &[String]) -> Config {
    let query = args[1].clone();
    let filename = args[2].clone();

    Config {
        query, filename
    }
}

I would like to move the values into the Config struct to avoid the copy.

I tried moving the slice:

fn parse_config(args: [String]) -> Config

But got the error:

the trait `std::marker::Sized` is not implemented for `[std::string::String]`

Which makes sense because it's now reading as an unsized array. Next I tried generics:

fn parse_config<T: std::ops::Index<usize, Output=String> + Sized>(args: T) -> Config

Which gives the error:

cannot move out of indexed content

This also makes sense, as it would leave the vector in an invalid state. I could move the vector:

fn parse_config(mut args: Vec<String>) -> Config {
    let query = args.remove(1);
    // ...

But now the function is tied to the particular container Vector.

How would I write a function that consumes the vector and allows me to move out its contents, while still preserving the generic nature of the function?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Erik Uggeldahl
  • 1,076
  • 11
  • 23

1 Answers1

3

The answer is to use a consuming iterator:

    ...
    let config = parse_config(args.into_iter());
    ...

fn parse_config<T: Iterator<Item=String>>(mut args: T) -> Config {
    let query = args.nth(1).expect("First arg none");
    let filename = args.nth(0).expect("Second arg none");
    ...
}

Note that the nth function is advancing and consuming the iterator, so that instead of the second argument indexing at 2, it is now one iteration later, i.e. 0.

Erik Uggeldahl
  • 1,076
  • 11
  • 23
  • 1
    `nth(0)` is usually written `next()`. Also note that your answer has nothing to do with the collection being "indexable". – Shepmaster Jul 09 '17 at 14:21
  • No need for the `collect` above, and then the `into_iter` call; you should be able to pass `env::args()` directly to this function, no? – Steve Klabnik Jul 09 '17 at 17:06