Properly using clap values_of

Im trying to use values_of, but every example uses unwrap().collect() but in my case unwrap can fail:

 if matches.is_present("patterns") {
      let  patterns = matches.values_of("patterns").unwrap().collect();
      let paths = matches.values_of("paths").unwrap_or("./").collect(); //error

// more code ...

I need to get the values of paths or default to "./".

How can I do this?

values_of returns Option<Vec<&str>>, so in the unwrap_or you must provide the Vec too, as your error should probably hint:

let paths = matches.values_of("paths").unwrap_or(vec!["./"]).collect();

You can use Option::unwrap_or_else to avoid an allocation,

let paths = matches.values_of("paths").unwrap_or_else(|| vec!["./"]).collect();

Thanks to all for your answers,

Why using a closure to return the vector avoids allocations?

Because creating a closure does not run any code inside the closure. In particular, vec!["./"] will not be called unless values_of returns None.

If you want to learn how closures work, you can read my blog on the topic here

1 Like

let paths = matches.values_of("paths").unwrap_or(vec!["./"]).collect()

This give me this error:

mismatched types
expected struct `clap::Values`, found struct `std::vec::Vec`
note: expected type `clap::Values<'_>`
         found type `std::vec::Vec<&str>`

I have no idea how to convert from clap::Values<_> to Vec

I see, the allocation only occurs when the closure is called if the unwrap is successful then there is no need for the closure and therefore for the allocation.

Sorry, thinks that I was looking at something wrong.

You already do this by using collect, you just need to move this operation before the unwrap:

    if matches.is_present("patterns") {
        let patterns: Vec<_> = matches.values_of("patterns").unwrap().collect();
        let paths = matches
            .map(Values::collect) // or Iterator::collect
            .unwrap_or_else(|| vec![]);


1 Like

Thanks for the answer!!!!
Could you please explain me the solution or point me to where i can read something about that?
values_of returns an option that wraps a clap::Value type, right?
Is posible to iterate over an option type? What it does, to the option type?
Thanks again

Not a clap::Value, but clap::Values: documentation.

map in this case is not an iteration, strictly speaking, since Option hold only one item and not a sequence of items. It's more like the functor mapping: given a function from T1 to T2 and an instance of Functor<T1>, convert it to Functor<T2>. If you're not familiar with this kind of thing, just think of it as something which let us run a function (in this case - Values::collect, or equivalently - |val| val.collect()) inside the Option, without unwrapping it.

By the way, if you want to learn more about Option/Result combinators, here's a good interactive cheatsheet.

1 Like

thank you

Here you can use map_or_else like so,

let paths = matches
    .map_or_else(|| vec![], Values::collect);

Option has a lot of fun little functions!


I see!!!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.