Reputation: 6350
Is there a good way to store a heterogeneous list of iterators or objects that can be converted to iterators that can be used multiple times? I've a situation where I'd like to iterate over a vector in multiple ways. Most often, these indices will come from a range or another vector, but that's not a guarantee. In addition, these indices will be used multiple times, so they must be reusable. Is there a good way to accomplish this? As one attempt, we have the code:
// Create a struct to hold a list of items that generate indices and a vector
struct Foo {
data: Vec<f64>,
index_list: Vec<Box<dyn Iterator<Item = usize>>>,
}
// Function that iterates over elements in the data
fn bar(f: &mut Foo) {
for indices in &mut f.index_list {
println!("Start of iteration");
for i in indices {
println!("{}", f.data[i])
}
}
}
// Test the routine
fn main() {
let mut f = Foo {
data: vec![0.1, 1.2, 2.3, 3.4],
index_list: vec![
Box::new((0..=1).into_iter()),
Box::new(vec![2, 1, 3].into_iter()),
],
};
// Want these two functions to not consume the iterators and produce the
// same result
bar(&mut f);
bar(&mut f);
}
This produces
Start of iteration
0.1
1.2
Start of iteration
2.3
1.2
3.4
Start of iteration
Start of iteration
which should be expected. The iterators from the Foo
structure are consumed the first time bar is called. I would like to pass f
to foo
in an immutable fashion, so that multiple calls to foo
will return the same result. I also strongly do not want to collect the indices into a vector since the actual use case will involve a very long list of indices that themselves are very large. As such, there are memory limitations that are not necessarily present in this example.
Anyway, removing the mutability above gives a compiler error:
error[E0277]: `&Box<dyn Iterator<Item = usize>>` is not an iterator
--> src/main.rs:14:18
|
14 | for i in indices {
| ^^^^^^^ `&Box<dyn Iterator<Item = usize>>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&Box<dyn Iterator<Item = usize>>`
= note: `Iterator` is implemented for `&mut std::boxed::Box<dyn std::iter::Iterator<Item = usize>>`, but not for `&std::boxed::Box<dyn std::iter::Iterator<Item = usize>>`
= note: required because of the requirements on the impl of `IntoIterator` for `&Box<dyn Iterator<Item = usize>>`
= note: required by `into_iter`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
Attempting to clone indices gives:
error[E0277]: `&Box<dyn Iterator<Item = usize>>` is not an iterator
--> src/main.rs:14:18
|
14 | for i in indices.clone() {
| ^^^^^^^^^^^^^^^ `&Box<dyn Iterator<Item = usize>>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&Box<dyn Iterator<Item = usize>>`
= note: `Iterator` is implemented for `&mut std::boxed::Box<dyn std::iter::Iterator<Item = usize>>`, but not for `&std::boxed::Box<dyn std::iter::Iterator<Item = usize>>`
= note: required because of the requirements on the impl of `IntoIterator` for `&Box<dyn Iterator<Item = usize>>`
= note: required by `into_iter`
error: aborting due to previous error
and attempting to use cloned
on index_list
gives:
error[E0277]: the trait bound `dyn Iterator<Item = usize>: Clone` is not satisfied
--> src/main.rs:12:40
|
12 | for indices in f.index_list.iter().cloned() {
| ^^^^^^ the trait `Clone` is not implemented for `dyn Iterator<Item = usize>`
|
= note: required because of the requirements on the impl of `Clone` for `Box<dyn Iterator<Item = usize>>`
error[E0277]: the trait bound `dyn Iterator<Item = usize>: Clone` is not satisfied
--> src/main.rs:12:20
|
12 | for indices in f.index_list.iter().cloned() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `dyn Iterator<Item = usize>`
|
= note: required because of the requirements on the impl of `Clone` for `Box<dyn Iterator<Item = usize>>`
= note: required because of the requirements on the impl of `Iterator` for `Cloned<std::slice::Iter<'_, Box<dyn Iterator<Item = usize>>>>`
= note: required because of the requirements on the impl of `IntoIterator` for `Cloned<std::slice::Iter<'_, Box<dyn Iterator<Item = usize>>>>`
= note: required by `into_iter`
error: aborting due to 2 previous errors
Upvotes: 1
Views: 340
Reputation: 117771
You can store a list of functions that return those iterators:
struct Foo<'a> {
data: Vec<f64>,
index_list: Vec<&'a dyn Fn() -> Box<dyn Iterator<Item = usize>>>,
}
fn bar(f: &mut Foo) {
for indices in &mut f.index_list {
println!("Start of iteration");
for i in indices() {
println!("{}", f.data[i])
}
}
}
fn main() {
let mut f = Foo {
data: vec![0.1, 1.2, 2.3, 3.4],
index_list: vec![
&|| Box::new((0..=1).into_iter()),
&|| Box::new(vec![2, 1, 3].into_iter()),
],
};
bar(&mut f);
bar(&mut f);
}
Upvotes: 3