Assume I have a struct like this
struct Foo {
a: i32,
b: f64,
c: String,
d: u8,
}
And bar
is of type Vec<Foo>
, which already contains many elements.
I would like to have three iterators of Foo
's first three fields (a
, b
and c
) respectively. I know how to use unzip
to achieve unpacking bar
to its fields .
let (vec_a, (vec_b, vec_c)): (Vec<i32>, (Vec<f64>, Vec<String>)) = bar
.iter()
.map(|elem| (elem.a, (elem.b, elem.c)))
.unzip();
But I don't know how to make iterators in ways alike.
let (iter_a, (iter_b, iter_c)): (
Box<dyn Iterator<Item = i32>>,
(Box<dyn Iterator<Item = f64>>, Box<dyn Iterator<Item = String>>),
) = bar.iter().map(|elem| (elem.a, (elem.b, elem.c)));
This results in error[E0308]: mismatched types
. Did I miss something or I just cannot use the syntax above at all.
Also as another question, unzip
can only unzip pairs, if the struct has many fields, the nested level will soon explode, is there any better way to achieve my goal?
I would simply make three distinct iterators.
In the example below, the first part uses .unzip()
as you did but fixes some errors.
.iter()
prevents from consuming elem.c
.
If we do not want to use .into_iter()
, we need either a reference (.as_str()
) or a clone (.clone()
).
The second part simply makes three distinct iterators. It's the solution I prefer, as stated in the first sentence.
The third part tries to keep your original idea: start from one iterator and expect to split it in three.
It could make sense if the process of obtaining the initial iterator was quite complicated and we didn't want to repeat it three times.
In this case, we have to clone the initial iterator itself and add a final .map()
stage to each one.
struct Foo {
a: i32,
b: f64,
c: String,
_d: u8,
}
fn show_iter<I, E>(
title: &str,
iter: I,
) where
I: Iterator<Item = E>,
E: std::fmt::Debug,
{
print!("{}:", title);
for e in iter {
print!(" {:?}", e);
}
println!();
}
fn main() {
let bar = vec![
Foo {
a: 1,
b: 2.3,
c: "X".to_owned(),
_d: 4,
},
Foo {
a: 2,
b: 3.4,
c: "Y".to_owned(),
_d: 5,
},
Foo {
a: 3,
b: 4.5,
c: "Z".to_owned(),
_d: 6,
},
];
{
// or use vec_c: Vec<String> and elem.c.clone()
let (vec_a, (vec_b, vec_c)): (Vec<i32>, (Vec<f64>, Vec<&str>)) = bar
.iter()
.map(|elem| (elem.a, (elem.b, elem.c.as_str())))
.unzip();
println!("vec_a: {:?}", vec_a);
println!("vec_b: {:?}", vec_b);
println!("vec_c: {:?}", vec_c);
}
println!("~~~~~~~~~~~~~~~~");
{
let iter_a = bar.iter().map(|elem| elem.a);
let iter_b = bar.iter().map(|elem| elem.b);
// or use elem.c.clone()
let iter_c = bar.iter().map(|elem| elem.c.as_str());
show_iter("iter_a", iter_a);
show_iter("iter_b", iter_b);
show_iter("iter_c", iter_c);
}
println!("~~~~~~~~~~~~~~~~");
{
// or use elem.c.clone()
let iter_abc =
bar.iter().map(|elem| (elem.a, elem.b, elem.c.as_str()));
let iter_a = iter_abc.clone().map(|(a, _b, _c)| a);
let iter_b = iter_abc.clone().map(|(_a, b, _c)| b);
let iter_c = iter_abc.map(|(_a, _b, c)| c);
show_iter("iter_abc iter_a", iter_a);
show_iter("iter_abc iter_b", iter_b);
show_iter("iter_abc iter_c", iter_c);
}
}
/*
vec_a: [1, 2, 3]
vec_b: [2.3, 3.4, 4.5]
vec_c: ["X", "Y", "Z"]
~~~~~~~~~~~~~~~~
iter_a: 1 2 3
iter_b: 2.3 3.4 4.5
iter_c: "X" "Y" "Z"
~~~~~~~~~~~~~~~~
iter_abc iter_a: 1 2 3
iter_abc iter_b: 2.3 3.4 4.5
iter_abc iter_c: "X" "Y" "Z"
*/