'reading an integer from the command line using rust
I have the following code to create a string of length n+1, where n is passed as a command line argument:
let args: Vec<String> = env::args().collect();
let min_no_of_bytes = &args[2].parse::<u64>();
let no_of_times_to_repeat = min_no_of_bytes as usize;
let mut inputtext='0'.to_string().repeat(no_of_times_to_repeat);
But I get the following error during compile:
error[E0606]: casting `&Result<u64, ParseIntError>` as `usize` is invalid
--> src/main.rs:33:17
|
33 | let temp = min_no_of_bytes as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: cast through a raw pointer first
Please advise. Thanks.
Solution 1:[1]
There are several problems that are unhandled in the original code.
argsis not guaranteed to have a second argument, therefore&args[2]could panic- the string at
&args[2]is not guaranteed to be a parseable number and thereforeparsereturns aResult, which may carry an error.
Direct array access is discouraged in general, in Rust. It is of course possible, but it can panic and is slow. So if you want to access an array, you would usually use iterators or the .get() function.
Handling all the errors
First, I'd recommend handling all the errors before we make it a bit more pretty. The rust compiler should force you to do that if you avoid direct array accesses and unwrap:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let arg = match args.get(2) {
Some(val) => val,
None => {
println!("Not enough arguments provided!");
return;
}
};
let min_no_of_bytes = match arg.parse::<u64>() {
Ok(val) => val,
Err(e) => {
println!("Unable to parse number from argument: {}", e);
return;
}
};
let no_of_times_to_repeat = min_no_of_bytes as usize;
let inputtext = '0'.to_string().repeat(no_of_times_to_repeat);
println!("{}", inputtext);
}
Running this program with: cargo run -- bla 3 prints 000.
Setting up proper error handling
If you realize we now have println!(...error) and return right next to each other. Instead, we could directly return an error from main to combine those two actions. Rust will then automatically print the error.
In order to accept any error type, this can be done with the type Result<(), Box<dyn Error>> or wrapping types like anyhow.
I'll provide the anyhow solution here, because I think it's in general better to use a wrapper library instead of Box<dyn Error> directly.
use anyhow::{anyhow, Result};
use std::env;
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
let arg = args
.get(2)
.ok_or(anyhow!("Not enough arguments provided!"))?;
let min_no_of_bytes = arg.parse::<u64>()?;
let no_of_times_to_repeat = min_no_of_bytes as usize;
let inputtext = '0'.to_string().repeat(no_of_times_to_repeat);
println!("{}", inputtext);
Ok(())
}
Note the ? operator here, which is a quick way of saying "if this is an Err value, return it from the function, otherwise unwrap the value". Basically the safe, error-propagating version of .unwrap().
Using a proper command line parsing library
I won't go into further detail here, but if your projects get a little bigger than 10 lines of code, you will quickly discover that writing argument parsing manually is pretty tedious.
There are several libraries that make this process very easy. The one I'd recommend at the time of writing this is clap:
use clap::Parser;
/// A program that does things
#[derive(Parser, Debug)]
#[clap(version, about)]
struct Args {
/// Minimum number of bytes
#[clap(short)]
min_no_of_bytes: u64,
}
fn main() {
let args = Args::parse();
let no_of_times_to_repeat = args.min_no_of_bytes as usize;
let inputtext = '0'.to_string().repeat(no_of_times_to_repeat);
println!("{}", inputtext);
}
This requires clap = { version = "3.1", features = ["derive"] } in your Cargo.toml.
Running it with cargo run -- -m 3 gives you 000.
The cool side effect is that you get a help message for free:
cargo run -- -h
my-program 0.1.0
A program that does things
USAGE:
my-program.exe -m <MIN_NO_OF_BYTES>
OPTIONS:
-h, --help Print help information
-m <MIN_NO_OF_BYTES> Minimum number of bytes
-V, --version Print version information
Solution 2:[2]
The following worked:
let args: Vec<String> = env::args().collect();
let min_no_of_bytes = *(&args[2].parse::<usize>().unwrap());
let mut inputtext='0'.to_string().repeat(no_of_times_to_repeat);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Finomnis |
| Solution 2 |
