Macro for generating iterators for custom container types?

My crate that I'm working on has a bunch of non-generic containers. Each container offers various ways to iterate through it, and according to the API Guidelines, each iterator method should return a type of the same name. Most of the time, I am simply iterating over a standard collection. So I end up writing a lot of this:

use delegate::delegate;

pub struct Processes {
    matched: Vec<Process>,
    ...
}

impl Processes {
    pub fn matched(&self) -> Matched {
        Matched(self.matched.iter())
    }
    ...
}

pub struct Matched<'a>(slice::Iter<'a, Process>);
impl<'a> Iterator for Matched<'a> {
    type Item = &'a Process;
    delegate! {
        to self.0 {
            fn next(&mut self) -> Option<Self::Item>;
            fn size_hint(&self) -> (usize, Option<usize>);
        }
    }
}

I am already using the delegate macro but it still feels like too much boilerplate. Is there a macro that can help even more in this situation?

I don't think this guideline needs to be followed strictly as a rule, unless your use case requires the iterators to be namable, you can just use RPIT to return an opaque Iterator type:

impl Processes {
    pub fn matched(&self) -> impl Iterator<Item = &Process> {
        self.matched.iter()
    }
}

How much semver flexibility do you really need?

If you're fine with it being a semver break to change from it being a slice iterator, you could always just add a type Matched<'a> = slice::Iter<'a, Process>; convenience alias, and thus not need to forward anything.

I mostly agree with the others here. I don't think it's actually needed to wrap your types like this. Usually, when I need iterators I just use it as an opaque iterator type as @nerditation suggests. That being said, I've been meaning to try my had at writing macros myself. They've been a bit mystical to me for too long. Here's what I came up with:

use delegate::delegate;

macro_rules! wrapped_iterator {
    ($struct:ty, $member:ident,  $wrapper:ident, $wrapped:ty) => {
        impl $struct {
            pub fn $member(&self) -> $wrapper {
                $wrapper(self.$member.iter())
            }
        }

        pub struct $wrapper<'a>($wrapped);

        impl<'a> Iterator for $wrapper<'a> {
            type Item = <$wrapped as Iterator>::Item;

            delegate! {
                to self.0 {
                    fn next(&mut self) -> Option<Self::Item>;
                    fn size_hint(&self) -> (usize, Option<usize>);
                }
            }
        }
    };
}

pub struct Process;

pub struct Processes {
    matched: Vec<Process>,
}

wrapped_iterator! {Processes,  matched, Matched, std::slice::Iter<'a, Process>}

the wrapped_iterator! invocation expands to this code:

impl Processes {
    pub fn matched(&self) -> Matched {
        Matched(self.matched.iter())
    }
}
pub struct Matched<'a>(std::slice::Iter<'a, Process>);

impl<'a> Iterator for Matched<'a> {
    type Item = <std::slice::Iter<'a, Process> as Iterator>::Item;
    delegate! {
        to self.0 {
            fn next(&mut self)->Option<Self::Item>;
            fn size_hint(&self)->(usize,Option<usize>);
        }
    }
}
1 Like

Out of curiosity, what does RPIP mean?

oopsie,got a typo, should be RPIT: Return Position Impl Trait