Can trait objects of type IntoIterator be boxed and held inside of a structure?

Viewed 359

Can trait objects of type IntoIterator be boxed and held inside of a structure? I've a situation where I'd like to store a vector of objects that can be turned into iterators. My attempt at this was the code

struct Foo {   
    foo: Vec<Box<dyn IntoIterator<Item = usize>>>,
}                                                            
                 
fn main() {                                                                     
    let _ = Foo {                                                               
        foo: vec![Box::new(1..2), Box::new(vec![2,4,4])],                       
    };                                                                          
}

However, this does not compile and produces the error:

error[E0191]: the value of the associated type `IntoIter` (from trait `IntoIterator`) must be specified
 --> src/main.rs:6:22
  |
6 |     foo: Vec<Box<dyn IntoIterator<Item = usize>>>,
  |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `IntoIterator<Item = usize, IntoIter = Type>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0191`.

I am aware that most of the time when we want to use IntoIterator we instead pass a generic into the structure. However, in this case, the items that I'd like to store do not have the same time. As such, is there a way to store a heterogenous collection of objects that can all be turned into an iterator of the same type?

1 Answers

What you're wanting to do doesn't make a lot of sense to me. But it would be easy to box each Iterator instead of each IntoIterator.

struct Foo {   
    foo: Vec<Box<dyn Iterator<Item = usize>>>,
}                                                            
                 
fn main() {                                                                     
    let _ = Foo {                                                               
        foo: vec![Box::new((1..2).into_iter()), Box::new(vec![2,4,4].into_iter())],                       
    };                                                                          
}

The problem you're running into is that IntoIterator is defined like this:

pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

In order for the compiler to emit monomorphic code, you have to specify both the Item type and the IntoIter type in the dyn specifier in the declaration of the foo field. But different types that implement IntoIterator have differing IntoIter types.

If it's really important to you to box up IntoIterator objects before they are actually turned into Iterators, then you have to add a new wrapper that transforms all the diverse IntoIter types into a Box<dyn Iterator>. Like this:

use std::iter::IntoIterator;

struct GenericIntoIterator<I: IntoIterator>(I);

impl<I: 'static + IntoIterator> std::iter::IntoIterator for GenericIntoIterator<I>
{
    type Item = <I as IntoIterator>::Item;
    type IntoIter = Box<dyn 'static + Iterator<Item = Self::Item>>;
    fn into_iter(self) -> Self::IntoIter {
        Box::new(self.0.into_iter())
    }
}

struct Foo {   
    foo: Vec<Box<dyn 'static + IntoIterator<Item = usize, IntoIter = Box<dyn 'static + Iterator<Item=usize>>>>>,
}                                                            
                 
fn main() {                                                                     
    let _ = Foo {                                                               
        foo: vec![Box::new(GenericIntoIterator(1..2)), Box::new(GenericIntoIterator(vec![2,4,4]))],                       
    };                                                                          
}
Related