Trait for Collection of Items Implementing Trait

Hi, I have this trait (dyn-safe):

trait Object {
    type Event;
}
impl<'a, T: Object + ?Sized> Object for &'a T {
    type Event = T::Event;
}

And I would like to make a trait that represents a collection of objects. From the collection a DoubleEndedIterator can be created.

It must be implemented for tuples of different types that implement Object (when they all have the same Event type) and vectors, where the contained types don't have to be 'static. It must also not depend on the lifetime of the container that holds it (if it uses a lifetime parameter you must be able to use it with HRTBs).

So if the trait is Collection<'a> this must compile:

fn assert_impls<'a, T: Object>() {
    fn x<C: for<'a> Collection<'a>>() {}

    x::<(T,)>();
    x::<(&'a T,)>();
    x::<Vec<T>>();
    x::<Vec<&'a T>>();
}

I've been struggling with this for ages, I would greatly appreciate help.

Since tuples can contain different types, they don't implement Iterator (or IntoIterator) as provided. You would need your own implementations. I think it may be possible for IntoIterator, but quite tedious indeed. Something like, for each tuple length, (&(...)).into_iter() returns a &[&dyn Object] or such.

Perhaps I'm not understanding, but that sounds like a contradiction to me. The objects contained must be at least as long as the container that holds them.

1 Like

Yup, it's going to be a custom trait.

Sorry, that was bad wording - by "container that holds it" I meant the type that holds the container that holds each of the objects - so it must either not have a lifetime parameter or have a lifetime parameter that works with HRTBs, just not require fn foo<'a>(&'a self) where T: Collection<'a> or something, since that won't work in traits.

Macros are an excellent tool for reducing tedium:

trait Collection<'a, Ev:'a> {
    type Iter: DoubleEndedIterator<Item=&'a (dyn 'a+Object<Event=Ev>)>;
    fn iter_dyn(&'a self)->Self::Iter;
}

macro_rules! impl_collection_for_tuple {
    ($($X:tt,)*) => {
        impl<'a,Ev:'a,$($X,)*> Collection<'a,Ev> for ($($X,)*)
        where Self:'a,
              $($X:Object<Event=Ev>,)* {
            type Iter = Box<dyn 'a+DoubleEndedIterator<Item=&'a (dyn 'a+Object<Event=Ev>)>>;
            fn iter_dyn(&'a self)->Self::Iter {
                #[allow(non_snake_case)]
                let ( $(ref $X,)* ) = self;

                #[allow(unused_imports)]
                use std::iter::{once,empty};
                Box::new(empty()
                    $( .chain(once($X as &(dyn 'a+Object<Event=Ev>))) )*
                )
            }
        }
    }
}

impl_collection_for_tuple!();
impl_collection_for_tuple!(A,);
impl_collection_for_tuple!(A,B,);
impl_collection_for_tuple!(A,B,C,);
impl_collection_for_tuple!(A,B,C,D,);

(Playground)

1 Like

What would that solution look like when implemented on vectors? I tried it before and had trouble.

It would be something like this, but it won't work with HRTBs ("not general enough"):

impl<'a, Ev:'a, T:'a + Object<Event=Ev>> Collection<'a, Ev> for [T] {
    type Iter = std::iter::Map<<&'a [T] as IntoIterator>::IntoIter,
                               fn(&'a T)->&'a (dyn 'a+Object<Event=Ev>) >;
    fn iter_dyn(&'a self)->Self::Iter {
        self.into_iter().map(|x| x as &'a (dyn 'a+Object<Event=Ev>))
    }
}

impl<'a, Ev:'a, T:Object<Event=Ev>> Collection<'a, Ev> for Vec<T>
where [T]: Collection<'a, Ev> {
    type Iter = <[T] as Collection<'a, Ev>>::Iter;
    fn iter_dyn(&'a self)->Self::Iter { (*self).iter_dyn() }
}

(Playground)

I'm probably missing something, but this section of the book seems pretty applicable: Rust Book

Yeah, that's the issue I was having. Do you know how to make it work with HRTBs?

I fixed the issue, it's not that efficient but it works;

pub trait Collection<'a> {
    type Event: 'a;
    type Iter: Iterator<Item = &'a dyn Element<Event = Self::Event>> + DoubleEndedIterator + 'a;

    fn iter(&'a self) -> Self::Iter;
}

impl<'a, O: Object> Collection<'a> for Vec<O>
where
    O::Event: 'a,
{
    type Event = O::Event;
    type Iter = Box<
        dyn vec_iter_impl::IteratorAndDoubleEnded<Item = &'a dyn Object<Event = Self::Event>> + 'a,
    >;

    fn iter(&'a self) -> Self::Iter {
        Box::new((**self).iter().map(
            |o| -> &'a dyn Object<Event = Self::Event> { o },
        ))
    }
}

mod vec_iter_impl {
    pub trait IteratorAndDoubleEnded: Iterator + DoubleEndedIterator {}

    impl<T: Iterator + DoubleEndedIterator> IteratorAndDoubleEnded for T {}
}
1 Like