Home > Mobile >  How can I create a struct that takes an iterator?
How can I create a struct that takes an iterator?

Time:03-21

I would like to create a struct that can hold any kind of iterator. A simplified attempt of my actual code is below:

struct MyStruct<I> {
    my_iter: I,
}

impl<I> MyStruct<I>
where 
    I: std::iter::Iterator<Item = usize>,
{
    fn new(start: usize) -> Self {
        let v: Vec<usize> = vec![start 1, start 2, start 3];
        Self {
            my_iter: v.iter().map(|n| n*2),
        }
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7c7c68dff49b280bb639738233d357fd

Unfortunately I get the following error on compilation:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:12:22
   |
5  | impl<I> MyStruct<I>
   |      - this type parameter
...
12 |             my_iter: v.iter().map(|n| n*2),
   |                      ^^^^^^^^^^^^^^^^^^^^^ expected type parameter `I`, found struct `Map`
   |
   = note: expected type parameter `I`
                      found struct `Map<std::slice::Iter<'_, usize>, [closure@src/main.rs:12:35: 12:42]>`

error[E0284]: type annotations needed: cannot satisfy `<_ as Iterator>::Item == usize`
  --> src/main.rs:18:13
   |
18 |     let _ = MyStruct::new(2);
   |             ^^^^^^^^^^^^^ cannot satisfy `<_ as Iterator>::Item == usize`
   |
note: required by a bound in `MyStruct::<I>::new`
  --> src/main.rs:7:28
   |
7  |     I: std::iter::Iterator<Item = usize>,
   |                            ^^^^^^^^^^^^ required by this bound in `MyStruct::<I>::new`
8  | {
9  |     fn new(start: usize) -> Self {
   |        --- required by a bound in this

Some errors have detailed explanations: E0284, E0308.
For more information about an error, try `rustc --explain E0284`.
error: could not compile `playground` due to 2 previous errors

I can't figure out from the error how to get what I want.

CodePudding user response:

There are two problems with your implementation:

  • The .iter() method borrows the vec, thus you are trying to store a reference to local variable, which is incorrect (use after free)
  • The generics are driven from "outside", but you want to specify them from inside the function, which is not possible.

So one option is to use a boxed iterator. This would allow you to both specify it from withing the function body and also use any iterator:

struct MyStruct {
    my_iter: Box<dyn Iterator<Item = usize>>,
}

impl MyStruct {
    fn new(start: usize) -> Self {
        let v: Vec<usize> = vec![start   1, start   2, start   3];
        Self {
            // note that we are using `.into_iter()` which does not borrow the vec, but converts it to an iterator!
            my_iter: Box::new(v.into_iter().map(|n| n * 2)),
        }
    }
}

Another option, if you insist on using generics, is to pass the iterator from outside:

use std::marker::PhantomData;

struct MyStruct<'l, I> {
    my_iter: I,
    _phantom: PhantomData<&'l I>,
}

impl<'l, I> MyStruct<'l, I>
where
    I: std::iter::Iterator<Item = usize>   'l,
{
    fn new(iter: I) -> Self {
        Self {
            my_iter: iter,
            _phantom: Default::default(),
        }
    }
}

fn main() {
    let mut data = vec![1, 2, 3];
    let x = MyStruct::new(data.iter().map(|x| *x   1));
}
  • Related