How do I convert a vector of strings to a vector of integers in a functional way?
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 collect
ing all of the values here, but if you only take(5)
of them, you would only parse 5 of the strings.
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`?
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.