Skip to content

Fun with associated types, Iterator, and IntoIterator #23069

Closed
@Gankra

Description

@Gankra

(The following is a reduced example)

I have a composite two-level structure which I want to iterate. This involves iterating the first level to obtain blocks of the second level, which are in turn iterated. We would like to update this code to use associated types.

My first attempt is as follows:

use std::iter::IntoIterator;

struct AbsIter<DListIter, VecDequeIter> {
    list_iter: DListIter,
    block_iter: VecDequeIter,
}

impl<VecDequeIter, DListIter> Iterator for AbsIter<DListIter, VecDequeIter> where
    VecDequeIter: Iterator,
    DListIter: Iterator,
    // adding Item=VecDequeIter::Item solves issue
    DListIter::Item: IntoIterator<IntoIter=VecDequeIter> 
{
    type Item = VecDequeIter::Item;
    fn next(&mut self) -> Option<VecDequeIter::Item> {
        let block = self.list_iter.next().unwrap();
        self.block_iter = block.into_iter();
        self.block_iter.next()
    }
}

fn main(){}

but this errors out with:

<anon>:17:33: 17:44 error: type mismatch resolving `<VecDequeIter as core::iter::Iterator>::Item == <<DListIter as core::iter::Iterator>::Item as core::iter::IntoIterator>::Item`:
 expected trait `core::iter::Iterator`,
    found trait `core::iter::IntoIterator` [E0271]
<anon>:17         self.block_iter = block.into_iter();
                                          ^~~~~~~~~~~

Which doesn't make any sense to me. It looks like it might be a bug. Still, it's easily resolved by making the change in the comment: adding Item=VecDequeIter::Item to the IntoIterator bound solves this.

I then notice that actually VecDequeIter need not be a generic argument, it follows from DListIter. Ok, let's do that:

use std::iter::IntoIterator;

struct AbsIter<DListIter, VecDequeIter> {
    list_iter: DListIter,
    block_iter: VecDequeIter,
}

impl<DListIter> Iterator for 
AbsIter<DListIter, <DListIter::Item as IntoIterator>::IntoIter> where
    DListIter: Iterator,
    DListIter::Item: IntoIterator,
{
    type Item = <DListIter::Item as IntoIterator>::Item;
    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        let block = self.list_iter.next().unwrap();
        self.block_iter = block.into_iter();
        self.block_iter.next()
    }
}

fn main(){}

Ignoring the apparently necessary as IntoIterator's, this triggers some clearly exponential behaviour in the coherence checker:

time: 0.000     parsing
time: 0.000     recursion limit
time: 0.000     gated macro checking
time: 0.000     configuration 1
time: 0.000     crate injection
time: 0.004     macro loading
time: 0.000     plugin loading
time: 0.000     plugin registration
time: 0.001     expansion
time: 0.000     complete gated feature checking
time: 0.000     configuration 2
time: 0.000     maybe building test harness
time: 0.000     prelude injection
time: 0.000     checking that all macro invocations are gone
time: 0.000     assigning node ids and indexing ast
time: 0.000     external crate/lib resolution
time: 0.000     language item collection
time: 0.004     resolution
time: 0.000     lifetime resolution
time: 0.000     looking for entry point
time: 0.000     looking for plugin registrar
time: 0.000     region resolution
time: 0.000     loop checking
time: 0.000     static item recursion checking
time: 0.000     type collecting
time: 0.000     variance inference
time: 16.663    coherence checking
time: 0.420     type checking
time: 0.110     const checking
time: 0.000     privacy checking
time: 0.000     stability index
time: 0.000     intrinsic checking
time: 0.000     effect checking
time: 0.001     match checking
time: 0.000     liveness checking
time: 0.212     borrow checking
time: 0.110     rvalue checking
time: 0.000     reachability checking
time: 0.000     death checking
time: 0.001     stability checking
time: 0.000     unused lib feature checking
time: 0.001     lint checking
time: 0.000     resolving dependency formats
time: 0.002     translation
  time: 0.000   llvm function passes
  time: 0.000   llvm module passes
  time: 0.009   codegen passes
  time: 0.000   codegen passes
time: 0.016     LLVM passes
  time: 0.387   running linker
time: 0.387     linking

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)I-compiletimeIssue: Problems and improvements with respect to compile times.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions