Search code examples
rustiterator

Clean way of taking first argument without modifiying args


This program should take arguments like so: <command> n1 n2 n3... e.g mode 1 2 3 3 4. I want to store the <command> argument string and pass the rest of the arguments into a constructor for parsing.

fn main() {
    let args = args();

    let command = args.skip(1).next().unwrap_or_else(|| {
        eprintln!("Command not found; expected one of \"mode\", \"mean\", \"median\" \"range\"");
        process::exit(1);
    });

    let set = DataSet::from_args(args).unwrap_or_else(|err| {
        eprintln!("error parsing values: {}", err);
        process::exit(1);
    });

    match command.to_lowercase().trim() {
        "mode" => println!("{:?}", set.mode()),
        _ => eprintln!("Command not recognised: \"{}\"", command),
    }
}

This code doesn't work because args.skip(1).next()... consumes args by adapting it into a Skip, so when I try to call DataSet::from_args(args) It is invalid because it was moved into skip.

What is the best way to achieve this? I've heard the itertools library could provide an nth method for Args but I'd prefer to do this without.


Solution

  • First, std has nth() method: you can use nth(1) instead of skip(1).next().

    Second, Iterator is implemented for &mut Iterator. So you can just call skip() on &mut args. There is even a method for that: by_ref():

        let command = args.by_ref().skip(1).next().unwrap_or_else(|| {
            eprintln!("Command not found; expected one of \"mode\", \"mean\", \"median\" \"range\"");
            process::exit(1);
        });