Properly using clap values_of

Hi,
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();
2 Likes

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

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

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.
Thanks!!!

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
            .values_of("paths")
            .map(Values::collect) // or Iterator::collect
            .unwrap_or_else(|| vec![]);
    }

Playground

2 Likes

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
    .values_of("paths")
    .map_or_else(|| vec![], Values::collect);

Option has a lot of fun little functions!

2 Likes

I see!!!

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