I'm trying to create a vector of String
s to test arg parsing (since this is what std::env::args()
returns) but struggling with how to do this concisely.
What I want:
let test_args = vec!["-w", "60", "arg"]; // should array of Strings
let expected_results = my_arg_parser(test_args);
This obviously doesn't work because the vectors contents are all &str
s.
Using String::from
but works but doesn't scale well and is ugly :)
let args = vec![String::from("-w"), String::from("60"), String::from("args")];
I could map over the references and return string objects, but this seems very verbose:
let args = vec!["-w", "60", "args"].iter().map(|x| x.to_string()).collect::<Vec<String>>();
Should I just create a helper function to do the conversion, or is there an easier way?
Using Vec::new() Method: let v : Vec<i64> = Vec::new(); Here v is the initialized vector that will contain the 64-bit integer datatype. It is initialized with help of the Vec::new() method.
C++11 also support std::begin and std::end for array, so a vector can also be initialized like static const int arr[] = {10,20,30}; vector<int> vec(begin(arr), end(arr)); .
You can use the to_string()
method directly on the literals:
let test_args = vec!["-w".to_string(), "60".to_string(), "arg".to_string()];
Otherwise a macro to do this would be as simple as:
macro_rules! vec_of_strings {
($($x:expr),*) => (vec![$($x.to_string()),*]);
}
See play.rust.org example
JDemler already provided a nice answer. I have two additional things to say:
First, you can also use into()
instead of to_string()
for all elements but the first. This is slightly shorter and also equivalent to to_string()
/String::from()
. Looks like this:
vec!["a".to_string(), "b".into(), "c".into()];
Second, you might want to redesign your arg parsing. I will assume here that you won't mutate the String
s you get from env::args()
. I imagine your current function to look like:
fn parse_args(args: &[String]) -> SomeResult { ... }
But you can make that function more generic by not accepting String
s but AsRef<str>
. It would look like this:
fn parse_args<T: AsRef<str>>(args: &[T]) -> SomeResult { ... }
In the documentation you can see that String
as well as str
itself implement that trait. Therefore you can pass a &[String]
and a &[&str]
into your function. Awesome, eh?
In similar fashion, if you want to accept anything that can be converted into an owned String
, you can accept <T: Into<String>>
and if you want to return either a String
or an &str
, you can use Cow
. You can read more about that here and here.
Apart from all that: there are plenty of good CLI-Arg parsers out there (clap-rs
, docopt-rs
, ...), so you might not need to write your own.
I agree that Lukas Kalbertodt's answer is the best — use generics to accept anything that can look like a slice of strings.
However, you can clean up the map
version a little bit:
Vec<String>
); you could specify just the collection (Vec<_>
). If you pass the result to a function that only accepts a Vec<String>
, then you don't need any explicit types at all; it can be completely inferred.s.into()
in the map
.fn do_stuff_with_args(args: Vec<String>) { println!("{}", args.len()) }
fn main() {
let args = ["-w", "60", "args"].iter().map(|&s| s.into()).collect();
do_stuff_with_args(args);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With