Defining a trait which returns an iterator of a specific type

I want to define a trait that describes a collection of entities defined as a enum. The trait should, in the ideal world, state that things implementing it should be convertable be able to return an Iterator as well as implement IntoIterator. As far as I can tell both of these should be possible in one case using GATs.

In this case, the entities returned will always be the same type -- that is, I know up front, the Item of Iterator and IntoIterator. But I am simply not sure how to specify the type of an associated type in a second interface.

Using the example from the GAT explainer I want something like this:

trait UsizeIterable {
    // Type of item yielded up; will be a reference into `Self`.
    type Item<'collection>
    where
        Self: 'collection=usize;

    // Type of iterator we return. Will return `Self::Item` elements.
    type Iterator<'collection>: Iterator<Item = Self::Item<'collection>>
    where
        Self: 'collection;

    fn iter<'c>(&'c self) -> Self::Iterator<'c>;
    //           ^^                         ^^
    //
    // Returns a `Self::Iter` derived from `self`.
}

This fails, both in practice and in design. To be clear, I am not trying to define something that produces an Iterator<Item=usize> by default but something that will only ever produce an Iterator of this kind.

Any help in knowing how to do this would be great.

Do you have a fixed type you want to iterate over or not? Your code looks like it attempts to say it will only iterate over usize but then you implement the trait for a generic T.

If you only need your trait to be for a concrete type, you can just drop the Item associated type from your trait and specify your concrete type in the Iterator bounds.

Playground

#![feature(generic_associated_types)]

trait Iterable {
    // Type of iterator we return. Will return `Self::Item` elements.
    type Iterator<'collection>: Iterator<Item = &'collection usize>
    where
        Self: 'collection;

    fn iter<'c>(&'c self) -> Self::Iterator<'c>;
    //           ^^                         ^^
    //
    // Returns a `Self::Iter` derived from `self`.
}

// from before
struct Iter<'c> {
    data: &'c [usize],
}

impl<'c> Iterator for Iter<'c> {
    type Item = &'c usize;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some((prefix_elem, suffix)) = self.data.split_first() {
            self.data = suffix;
            Some(prefix_elem)
        } else {
            None
        }
    }
}

impl Iterable for [usize] {
    type Iterator<'c> = Iter<'c>;

    fn iter<'c>(&'c self) -> Self::Iterator<'c> {
        Iter { data: self }
    }
}

impl Iterable for Vec<usize> {
    type Iterator<'c> = Iter<'c>;

    fn iter<'c>(&'c self) -> Self::Iterator<'c> {
        Iter { data: self }
    }
}

fn count_twice<I: Iterable + ?Sized>(iterator: &I) -> usize {
    let mut count = 0;

    for _ in iterator.iter() {
        count += 1;
    }

    for _ in iterator.iter() {
        count += 1;
    }

    count
}

fn main() {
    let x: &[usize] = &[1, 2, 3];
    let c = count_twice(x);
    assert_eq!(c, 6);
    println!("c = {c}");

    let c = count_twice(&vec![1, 2, 3, 4, 5, 6]);
    assert_eq!(c, 12);
    println!("c = {c}");
}

Are you asking how to write the bound itself?

Thank you! My idea was right and syntax wrong.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.