How can i use Options in Rust? (Options are single letters with a dash before it.) => prog -q arg1 -z arg2

I need zu use Options in Rust.

This is how it is done in BASH:

Can some show me how it is done in Rust?

At first I thought that I would save all options in a tuple and then look in it to see which options were passed and then read the values ​​for them from the tuple. But I couldn't do that.

I use the clap crate to read my program's command line arguments and options. https://crates.io/crates/clap. Works a treat.

There are many other command line parsers to choose from: https://crates.io/search?page=1&per_page=10&q=command%20line%20arguments

There is a simple example of how to get access to the arguments in the standard library.

rustup doc std::env::args

This code will do the same as the bash script in the link. A starting point for you.


use std::env;

fn main() {
    // create a mutable iterator over the environment arguments
    let mut arguments = env::args();
    // the first argument  is the program name, skip it.
    arguments.next();
    // step through the iterator putting values in arg
    for arg in arguments {
        // match is like a case in bash or c
        match arg.as_str() {
            "-a" => println!("-a option passed"), // Message for -a option
            "-b" => println!("-b option passed"), // Message for -b option
            "-c" => println!("-c option passed"), // Message for -c option
            _ => println!("Option {} not recognized", arg), // In case you typed a different option other than a,b, c
        }
    }
}
1 Like

I recommend structopt:

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Options {
  #[structopt(short = "q")]
  foo: String,
  #[structopt(short = "z")]
  bar: String,
}


fn main() {
    let options = Options::from_args();
    println!("options: {:?}", options);
}
3 Likes

Thank-you!
That's great. Also, can you show me how to implement this code using Rust?

#!/bin/bash

while [ "${#}" -ne "0" ]; do
        case "${1}" in
                -a)
                        OPTION_A=${2}
                        shift
                        ;;
                -b)
                        OPTION_B=${2}
                        shift
                        ;;
                *)
                        shift
                        ;;
        esac
done

echo "
OPTION_A=${OPTION_A}
OPTION_B=${OPTION_B}
"

that would work that way:

./test.sh -a Arg1 -b Arg2

OPTION_A=Arg1
OPTION_B=Arg2

I need the option after the dash-option in a Variable.

Can someone please tell me what they mean when it says:

Add clap to your Cargo.toml
[dependencies] clap = "3.0.0-beta.2"

I have 23 Cargo.toml files in my system and no matter where I enter them, the example code does not work.

     1      /usr/local/lib/rustlib/src/rust/library/proc_macro/Cargo.toml
     2      /usr/local/lib/rustlib/src/rust/library/test/Cargo.toml
     3      /usr/local/lib/rustlib/src/rust/library/alloc/Cargo.toml
     4      /usr/local/lib/rustlib/src/rust/library/rustc-std-workspace-std/Cargo.toml
     5      /usr/local/lib/rustlib/src/rust/library/rustc-std-workspace-alloc/Cargo.toml
     6      /usr/local/lib/rustlib/src/rust/library/rustc-std-workspace-core/Cargo.toml
     7      /usr/local/lib/rustlib/src/rust/library/panic_abort/Cargo.toml
     8      /usr/local/lib/rustlib/src/rust/library/backtrace/Cargo.toml
     9      /usr/local/lib/rustlib/src/rust/library/panic_unwind/Cargo.toml
    10      /usr/local/lib/rustlib/src/rust/library/unwind/Cargo.toml
    11      /usr/local/lib/rustlib/src/rust/library/stdarch/examples/Cargo.toml
    12      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/core_arch/Cargo.toml
    13      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/stdarch-test/Cargo.toml
    14      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/stdarch-gen/Cargo.toml
    15      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/std_detect/Cargo.toml
    16      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/stdarch-verify/Cargo.toml
    17      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/simd-test-macro/Cargo.toml
    18      /usr/local/lib/rustlib/src/rust/library/stdarch/crates/assert-instr-macro/Cargo.toml
    19      /usr/local/lib/rustlib/src/rust/library/stdarch/Cargo.toml
    20      /usr/local/lib/rustlib/src/rust/library/term/Cargo.toml
    21      /usr/local/lib/rustlib/src/rust/library/profiler_builtins/Cargo.toml
    22      /usr/local/lib/rustlib/src/rust/library/core/Cargo.toml
    23      /usr/local/lib/rustlib/src/rust/library/std/Cargo.toml

And why do I have to enter a version number there? I always want the latest version. Is that possible with such an entry in the same Cargo.toml file?

[dependencies.clap]
git = "https://github.com/clap-rs/clap.git"

Sorry for so many questions, but I only started Rust 3 days ago.

It means the Cargo.toml in the root directory of your cargo project, same place where you have your src directory. If you're working with plain rs files with no project, make a new one with cargo new.

You can generally use "*" in place of a version number to always get the latest release, but this is a little hazardous because a new release is not necessarily compatible with your code anymore. Plus, if you want to use beta releases, * does not consider those, so you'll need to specify the full version if you want to use one. Using a git dependency like in your post is another option, but your code may again break when the repository changes.

Thank-you!

Hello, I've made a little progress. This program already works, but it can't do everything I want.

$ cd ~/Rust && ( rm -f Argumente_1 ; rustc Argumente_1.rs; ~/Rust/Argumente_1 -a Romeo -b Julia -c Cäsar)
Das 0. Vektorelement: /home/manfred/Rust/Argumente_1
Das 1. Vektorelement: -a
Der Wert hinter '-a' lautet: Romeo
Das 2. Vektorelement: Romeo
Das 3. Vektorelement: -b
Der Wert hinter '-b' lautet: Julia
Das 4. Vektorelement: Julia
Das 5. Vektorelement: -c
Der Wert hinter '-c' lautet: Cäsar
Das 6. Vektorelement: Cäsar

Argumente_1.rs

use std::env;

fn main() {
    let vector: Vec<String> = env::args().collect();
    let l = vector.len();

    for i in 0..l {
        println!("Das {}. Vektorelement: {}", i, &vector[i]);

        if &vector[i] == "-a" {
            let p = i + 1;
            if p < l {
                println!("Der Wert hinter '-a' lautet: {}", &vector[p]);
            }
        }
        if &vector[i] == "-b" {
            let p = i + 1;
            if p < l {
                println!("Der Wert hinter '-b' lautet: {}", &vector[p]);
            }
        }
        if &vector[i] == "-c" {
            let p = i + 1;
            if p < l {
                println!("Der Wert hinter '-c' lautet: {}", &vector[p]);
            }
        }
    }
}

But this program is supposed to store the required values ​​in variables (aaa, bbb, ccc), but it doesn't. What am I doing wrong?

use std::env;

fn main() {
    let vector: Vec<String> = env::args().collect();
    let l = vector.len();
    let mut aaa;
    let mut bbb;
    let mut ccc;

    for i in 0..l {
        println!("Das {}. Vektorelement: {}", i, &vector[i]);

        if &vector[i] == "-a" {
            let p = i + 1;
            if p < l {
                aaa = &vector[p];
            }
        }
        if &vector[i] == "-b" {
            let p = i + 1;
            if p < l {
                bbb = &vector[p];
            }
        }
        if &vector[i] == "-c" {
            let p = i + 1;
            if p < l {
                ccc = &vector[p];
            }
        }
    }

    println!("Alpha: {}", aaa);
    println!("Beta : {}", bbb);
    println!("Gamma: {}", ccc);
}

I know, that ist working:

...
    if &vector[i] == "-a" {
        let p = i + 1;
        if p < l {
            aaa = &vector[p];
            println!("Alpha: {}", aaa);
        }
    }
...

but I need the variables (aaa, bbb, ccc) outside of the for loop.

And in the end I would like to outsource the repeated if-queries to a function, but unfortunately I cannot transfer the content of "vector".

The compiler tells you that aaa, bbb and ccc might be uninitialized so you cannot just print them:

error[E0381]: borrow of possibly-uninitialized variable: `aaa`
  --> src/main.rs:31:27
   |
31 |     println!("Alpha: {}", aaa);
   |                           ^^^ use of possibly-uninitialized `aaa`

You could make those variables Options which can either contain a value (Some(value)) or not (None) and initialize them to the state where they don't contain a value:

Link to playground

fn main() {
    let vector: Vec<String> = vec!["-a".into(), "Test".into()];
    let l = vector.len();
    let mut aaa: Option<&String> = None;
    let mut bbb: Option<&String> = None;
    let mut ccc: Option<&String> = None;

    for i in 0..l {
        println!("Das {}. Vektorelement: {}", i, &vector[i]);

        if &vector[i] == "-a" {
            let p = i + 1;
            if p < l {
                aaa = Some(&vector[p]);
            }
        }
        if &vector[i] == "-b" {
            let p = i + 1;
            if p < l {
                bbb = Some(&vector[p]);
            }
        }
        if &vector[i] == "-c" {
            let p = i + 1;
            if p < l {
                ccc = Some(&vector[p]);
            }
        }
    }

    if let Some(a) = aaa {
        println!("Alpha: {}", a);
    }
    if let Some(b) = bbb {
        println!("Beta : {}", b);
    }
    if let Some(c) = ccc {
        println!("Gamma: {}", c);
    }
}
1 Like

Thank-you!

why do i need "Some"?

aaa = Some(&vector[p]);

can't I somehow store the value directly in "aaa"?
maybe as a string?

You can, but you'll need to store some sort of default value, like the empty String, in case a value for aaa is not supplied. You can't leave it undefined in Rust, you have to give it a value before you can use it.

I noticed something else about your implementation which may be unintentional. If someone passes in the arguments ("-a", "-b", "foo"), you set aaa to "-b" and you set bbb to "foo". But usually when arguments take a value, they consume that value, in which case you would want to set aaa to "-b" but leave bbb unset. (Or maybe given an error if "-b" doesn't make sense as a value for aaa.)

Here's a version to collect Strings that separates out the inspection (println!("Das ...")) from the logic to set the values. A version to collect Option<String> would be very similar.

Yes, an error message would make sense if you entered such an incorrect entry.

Yes, a string collecting version is good, but I don't understand it, so I didn't use it. How can I use it to evaluate individual values ​​from the input or save them in individual variables or constants?

will one (for arrays) or the other (for tuples) work:

println!("{}", args[0]);
println!("{}", args.0);

if the input is complete then it works properly.

~/Rust/Argumente_3 -a Romeo -b Julia -c Cäsar
DefaultedArgs {
    aaa: "Romeo",
    bbb: "Julia",
    ccc: "Cäsar",
}

But if the input is incomplete, then it does not work as intended.

~/Rust/Argumente_3 -a Romeo -b  -c Cäsar
DefaultedArgs {
    aaa: "Romeo",
    bbb: "-c",
    ccc: "",
}

i need it like this:

~/Rust/Argumente_3 -a Romeo -b  -c Cäsar
DefaultedArgs {
    aaa: "Romeo",
    bbb: "",
    ccc: "Cäsar",
}

So, you're going to have to look ahead to see if the next argument is a flag like "-c" or not. I think you really are going to be better off figuring out how to use something like clap or structopt like others mentioned above.

But since I took it this far, here's a version that will do that. It's possible using iterators too, but I went back to mostly using a Vec in case that is easier to understand.

2 Likes

Thank you, you are great!
This is exactly what i need.
:smiley:

After you get it down and figured out how do it then don't do it that way anymore. Use to "clap" or "structopt" to do the work.

How can I check whether the variable "aaa" is not used?

if aaa == () {
    println!("{}", aaa);
};

that's not how it works...

If aaa is an Option, then you can use the is_none method, or check if aaa == None.

if aaa.is_none() {
    println!("no value");
}