How to solve: value used after move

fn main() {
    let args= env::args().skip(1);
    let command = args.take(1).collect::<String>();

    match command.as_str() {
        "add" => {
            let task = args.take(1).collect::<String>();
        },
        "" | "help" | _ => {
            println!("{}", HELP);
        }
    }
}

The first take argument consumes the args so I can't use it again in the next lines. what's the solution?
Thank you.

.take(1) takes exclusive ownership of the iterator and then stops iterating, so it this case has an implied "and discard all the others" meaning.

You want let mut args = env::args().skip(1); and then use args.next() to get the next one.

You can still call .collect() on the iterator to get remaining elements that haven't been returned yet.

If you really need .take(), there's .by_ref()

which creates you a reference to the original iterator, without taking ownership, and this prevents other iterator methods from taking ownership of it too. The side effect of that is that .take() stops discarding the elements, because when it stops iterating and drops the iterator it's been using, it only drops a reference-to-the-iterator, and not the iterator with its contents.

1 Like

Thank you @kornel, your solution could solve the problem, but I thought the following is a better way in my case. I collected args as Vec<String> and then used it's elements using get method.


fn main() {
    let args:Vec<String> = env::args().skip(1).collect();

    let default_command = "help".to_string();
    let command = args.get(0).unwrap_or(&default_command);

    match command.as_str() {
        "add" => {
            let tasks = args.get(1..).unwrap();

        },
        "" | "help" | _ => {
            println!("{}", HELP);
        }
    }
}

First and foremost, I would strongly encourage you to use something like clap for processing command line arguments, at least for cases like this one: someone's already done the work. There's definitely a learning curve to it, but I've found that it pays off quickly.

However, if you'd prefer to write your own argument processing, can I recommend a different structure? Using the Iterator protocol a bit more directly will let you loop over options, and pull additional arguments for those that need them:

use std::env;

fn main() {
    let mut args = env::args().skip(1);

    while let Some(arg) = args.next() {
        match arg.as_str() {
            "add" => {
                let task = args.next().expect("add requires a task name");
                println!("task: {task}"); // replace with task logic
            }
            "help" => {
                println!("help");
                return; // replace with help logic
            }
            _ => {
                println!("usage");
                return; // replace with usage/unexpected argument logic
            }
        }
    }
    
    // Rest of program…
}

You can replace the while let with if let if a command is intended to be the first argument in the list, and if multiple commands would be a usage mistake.

1 Like

Thank you @derspiny,
Yes you're right about using clap, but because I'm just learning rust I preferred to write my own.

Nice structure, and in the "add" arm the tasks may be multiple arguments so what method should I use to get the rest of the args?

You can call args.next() repeatedly (checking for a None as you go; that's what the .expect(…) is doing, for example) to consume additional arguments. If the body of the add case grows to be too complex, you can also factor it out to its own function, taking args: &mut impl Iterator<Item=String>, plus any other useful arguments:

match arg.as_str() {
    "add" => add(&mut args),
    // …
}

// …

fn add(args: &mut impl Iterator<Item=String>) {
    let task = args.next().expect("add requires a task name");
    println!("task: {task}"); // replace with task logic
}

You can use the same technique of looping over the iterator to consume arguments inside of the extracted function, since the iterator is shared with the caller via a mutable reference.

If you might need to stop based on the value of an argument, without consuming that argument, then you can use .peekable() to transform the args iterator first, and use .peek() to look at the next argument without consuming it, or .next_if() to extract the next argument only as long as a condition holds.

1 Like