Cast Vec<T> to different types

Hi,
I'm writing a command line tool that uses clap. One of the arguments takes two values which have different types (usize and f64). Right now my code looks like this:

    let sphere: Vec<_> = matches.values_of("sphere").unwrap().collect();
    let origin_id: usize = sphere[0].parse().unwrap();
    let radius: f64 = sphere[1].parse().unwrap();

I am aware that clap provides a values_of_t method but that only seems to work if all values have the same type. My way works but it seems inelegant to me. Is there a more idiomatic way of doing it?

Adding error handling would be a big boost. You don't need to convert the iterator into a vector; doesn't add or take much away though.

Ultimately I need the two values stored somehow as different types and this is the only way I could come up with. If you have a good suggestions how to do it differently, I'm all ears :slight_smile:
Besides, I didn't include error handling yet because I'm only just writing the thing and wanted to get to that later. The correct number of values is handled by clap though.

The method that doesn't go through a vector mentioned by @jonh is like this:

let mut sphere_iter = matches.values_of("sphere").unwrap();
let origin_id: usize = sphere_iter.next().unwrap().parse().unwrap();
let radius: f64 = sphere_iter.next().unwrap().parse().unwrap();

Ah ok, so just iterating by hand.
So am I assuming correctly that there isn't a way to do it by destructuring the vector I built in the first place? I tried that earlier but I wasn't able to make it work.

Why don't revise your clap app definition to get two different arguments ? It seems (from your code sample) like you know you want 2 and only 2 things, so you don't need a dynamic container like Vec. sphere_origin and sphere_radius arguments.

If you were using structopt (or clap v3) I'd propose something like

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct SphereParams {
    origin: usize,
    radius: f64,
}

let params = SphereParams::from_args();

Ah, that was my bad then. I only gave a very minimal code example, in fact I have some 10 args, one of which is the mentioned sphere arg that takes two values. However, there are always multiple args present, some of which take values and others don't.

You could destructure the vec using if let or match with slice patterns. For example:

let (origin_id, radius): (usize, f64) = if let [o, r] = sphere[..] {
    (o.parse().unwrap(), r.parse().unwrap())
} else {
    panic!("wrong number of arguments")
};
1 Like

@mrubeck: This gives me an error because the compiler can't find o and r in this scope.

Did you copy and paste that exact code? It compiles in this Playground.

Ah you're right, I had an error typing it. Thank you for your help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.