wyer33
wyer33

Reputation: 6350

How to store a heterogenous list of iterators or objects that can be converted to iterators that can be used multiple times?

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

Answers (1)

orlp
orlp

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

Related Questions