What is the proper return type here?


pub trait Enum_Usize_T {
    fn enum_len() -> usize;
    fn to_usize(&self) -> usize;
    fn from_usize(t: usize) -> Self;

    fn iter() -> Map<std::ops::Range<usize>, _>
    where
        Self: Sized,
    {
        (0..Self::enum_len()).map(|x| Self::from_usize(x))
    }
}

-> impl Iterator<Item = Self> which needs return-position impl Trait in trait (RPITIT) that is going to be stable at the end of this month but has already been on beta and nightly.

2 Likes

Until that hits stable, can I do better than


    fn iter() -> Box<dyn Iterator<Item = Self>>
    where
        Self: Sized,
    {
        Box::new((0..Self::enum_len()).map(|x| Self::from_usize(x)))
    }

(would prefer to get rid of the Box)

Yes: write the iterator type out by hand.

You could try using associated types in the interim similar to Tower's Service trait, extract the other methods into a different trait and use a blanket impl for the provided method. Also function pointer for a concrete closure type.

trait EnumUsize {
    fn enum_len() -> usize;
    fn to_usize(&self) -> usize;
    fn from_usize(t: usize) -> Self;
}

trait EnumIterator: Sized {
    type Iter: Iterator<Item = Self>;
    
    fn iter() -> Self::Iter;
}

impl<T> EnumIterator for T 
where
    T: EnumUsize 
{
    type Iter = Box<dyn Iterator<Item = Self>>;

    fn iter() -> Self::Iter {
        Box::new((0..Self::enum_len()).map(Self::from_usize))
    }
}

// Other idea with concrete type
impl<T> EnumIterator for T 
where
    T: EnumUsize
{
    type Iter = Map<Range<usize>, fn(usize) -> Self>;

    fn iter() -> Self::Iter {
        (0..Self::enum_len()).map(Self::from_usize)
    }
}
4 Likes

LOL. Due to the return type mess, I have found it very annoying to build abstractions over iterators. They seem to really be unfriendly towards abstractions.

While I sympathise, this is just how it's been since before v1.0. I've lost count of the number of iterator adapters I've written out over the years.

You can box (at the expense of performance), or write the adapter by hand (at the expense of time). One day, hopefully, we'll have generator expressions. Until then, it's faster to just pick one of the solutions and get it over and done with. :slight_smile:

2 Likes

This is useful to know. It was not clear to me whether the mess was (1) me not knowing some cool technique or (2) this being a mess until RPITIT arrives.

Until we get TAIT/TAITIT or a return type notation of some sort, writing the adapter by hand is still more consumer-friendly than RPIT/RPITIT or async fn gen fn, etc, as anyone who wants to store or name the type for some other reason will otherwise have to box up and type-erase your unnameable type anyway (if it's even dyn safe). Or add a generic parameter to their type, maybe.

But if achieving the best consumer-friendly library design isn't a concern for your use case, it is a pain for little gain for now.

3 Likes

This is what the standard library does.

I can't think of a proc macro crate that automates the boilerplate of making these ...Iter types for you and implementing Iterator for them.

It feels like the kind of thing that would be useful but I suspect it's frought with its own difficulty!

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.