Search code examples
rustiteratorassociated-types

How to find the associated type `IntoIter` when implementing `IntoIterator` by using iterator composition in Rust?


I am currently trying to implement a structure of arrays. I want to implement the IntoIterator for the SOA in a way that yields the base structure on the fly, as if I was iterating over an array of structures. Here is the to types: the structure and its SOA,

struct Particle {
    x: f64,
    y: f64,
    v: [f64; 3]
}

struct ParticleSOA {
    x: Vec<f64>,
    y: Vec<f64>,
    v: Vec<[f64; 3]>,
}

By using the 'izip!' macro from itertools, It is quite easy to build an iterator by zipping the Vecs of my SOA and mapping the produced tuples to the original structure. However I can't find out what Type to give to the associated type IntoIterrequired for the IntoIterator trait.

impl IntoIterator for ParticleSOA{
    type Item = Particle;

    type IntoIter = ???;

    fn into_iter(self) -> Self::IntoIter {
       izip!(self.x,self.y,self.v).map(Particle::from)
    }
}

Is there a clever way to infer the type from my implementation of the into_iter function, Or my only choice is to work out the precise type created by the composition of iterator functions by hand?

EDIT

The secret was getting rid of closures, especially the one hidden in izip! Itertools has a multizip function that produces a usable type.

impl IntoIterator for ParticleSOA {
    type Item = Particle;

    fn into_iter(self) -> Self::IntoIter {
        multizip((self.x, self.y, self.v)).map(Particle::from)
    }

    type IntoIter = Map<
        itertools::Zip<(
            std::vec::IntoIter<f64>,
            std::vec::IntoIter<f64>,
            std::vec::IntoIter<[f64; 3]>,
        )>,
        fn((f64, f64, [f64; 3])) -> Particle,
    >;
}

Note : My struct conveniently implements From<(f64, f64, [f64; 3])>.


Solution

  • You could return a boxed iterator, so in case your implementation changes, the return type wouldn't need to:

    use itertools::izip; // 0.10.3
    
    struct Particle {
        x: f64,
        y: f64,
        v: [f64; 3]
    }
    
    struct ParticleSOA {
        x: Vec<f64>,
        y: Vec<f64>,
        v: Vec<[f64; 3]>,
    }
    
    impl IntoIterator for ParticleSOA{
        type Item = Particle;
    
        type IntoIter = Box<dyn Iterator<Item=Self::Item>>;
    
        fn into_iter(self) -> Self::IntoIter {
           Box::new(izip!(self.x,self.y,self.v).map(|(x, y, v)| Particle {x, y , v}))
        }
    }
    

    Playground

    Otherwise the type should be, as an approximation something like :

    Map<Zip<(f64, f64, [f64; 3])>, Fn>

    a rather complicated type.

    You can also make your own function, not implementing the trait:

    impl ParticleSOA {
        #[inline]
        fn into_iter(self) -> impl Iterator<Item=Particle> {
            izip!(self.x, self.y, self.v).map(|(x, y, v)| Particle { x, y, v })
        }
    }
    

    Playground