How do I convert a vector of strings to a vector o

2020-03-21 02:43发布

问题:

I'm trying to convert Vec<&str> to Vec<u16> but I can't figure out a functional way to do it.

let foo: &str = "1,2,3"; // Parsing a string here
let bar: Vec<&str> = foo.split(",").collect(); // Bar is a nice vector of &str's

I need to get bar into a Vec<u16>.

回答1:

There's an iterator adapter map! You'd use it like this:

let bar: Vec<u16> = foo.split(",").map(|x| x.parse::<u16>().unwrap()).collect();

parse is a library function that relies on the trait FromStr, and it can return an error, so we need to unwrap() the error type. (This is a good idea for a short example, but in real code, you will want to handle the error properly - if you have a value that's not a u16 there, your program will just crash).

map takes a closure that takes it's parameter by value and then returns the iterator obtained by lazily applying that function. You're collecting all of the values here, but if you only take(5) of them, you would only parse 5 of the strings.



回答2:

You haven't fully specified your problem. Specifically, what should happen when one of the strings cannot be parsed into a number? When you parse a number from a string using parse, it can fail. That is why the function returns a Result:

fn parse<F>(&self) -> Result<F, F::Err>
where
    F: FromStr,

Here's a solution that takes the vector, gets an iterator with iter, changes each item using map and ultimately returns a Result using collect. If the parsing was a success, you get an Ok. If any failed, you get an Err:

fn main() {
    let input = "1,2,3";
    let strings: Vec<_> = input.split(",").collect();

    let numbers: Result<Vec<u16>, _> = strings.iter().map(|x| x.parse()).collect();

    println!("{:?}", numbers);
}

Or you could remove failed conversions by filtering out Err values with flat_map:

fn main() {
    let input = "1,2,3";
    let strings: Vec<_> = input.split(",").collect();

    let numbers: Vec<u16> = strings.iter().flat_map(|x| x.parse()).collect();

    println!("{:?}", numbers);
}

Of course, it's a bit silly to convert the string into a vector of strings and then convert it again to a vector of integers. If you actually have a comma-separated string and want numbers, do it in one go:

fn main() {
    let input = "1,2,3";

    let numbers: Result<Vec<u16>, _> = input.split(",").map(|x| x.parse()).collect();

    println!("{:?}", numbers);
}

See also:

  • Why does `Option` support `IntoIterator`?


回答3:

My take as someone not really experienced in Rust yet.

fn main() {
    let foo: &str = "1,2,3"; // Parsing a string here
    let bar: Vec<&str> = foo.split(",").collect(); // Bar is a nice vector of &str's

    // here the magic happens
    let baz = bar.iter().map(|x| x.parse::<i64>());

    for x in baz {
        match x {
            Ok(i) => println!("{}", i),
            Err(_) => println!("parse failed"),
        }
    }
}

Note that since parse returns a Result, you have to extract the value from each parsed element. You might want to behave in a different way, e.g. filter only the succeeded results.