How to Require an Associated Type Bound

I'm trying to get something as close to lifetime-GATs as I can and I'm running into trouble with something that seems like it should be valid Rust:

trait HasIter<'t, T>
where
    T: 't,
{
    type Base: IntoIterator<Item = &'t T>;
}

trait IterRef<T>: for<'t> HasIter<'t, T, Base = &'t Self> {
    #[inline]
    fn iter<'t>(&'t self) -> <<Self as HasIter<'t, T>>::Base as IntoIterator>::IntoIter {
        // self.into_iter()
        todo!()
    }
}

The code fails to compile at the declaration of the iter function saying that &Self does not implement IntoIterator. But why is this so? The mere fact that IterRef extends HasIter should require that Base implements IntoIterator.

Here's the playground for this code snippet: Rust Playground. Any advice here would be very much appreciated!

I don't think associated type bounds propagate like that; they are not assumed to be true, they are checked to be satisfied by the implementor. Accordingly, this compiles.

2 Likes

Rustc doesn’t have a good understanding of type equalities such a the one given by the Base = &'t Self constraint, and won’t transfer a trait bound from Base to &'t Self accordingly. (But I believe there’s good potential for allowing such things in the future, eventually.) You can however work around these shortcomings with a bit of subtle helper traits and functions, as I recently demonstrated in this post

Modifying your playground accordingly could look as follows:

pub trait TypeIsEqual {
    type To: ?Sized;
}
impl<T: ?Sized> TypeIsEqual for T {
    type To = Self;
}

trait HasIter<'t, T: 't, Base> {
    type Base: IntoIterator<Item = &'t T> + TypeIsEqual<To = Base>;
}

trait IterRef<T>: for<'t> HasIter<'t, T, &'t Self> {
    #[inline]
    fn iter<'t>(&'t self) -> <<Self as HasIter<'t, T, &'t Self>>::Base as IntoIterator>::IntoIter {
        #[inline]
        fn helper<S: IntoIterator>(x: <S as TypeIsEqual>::To) -> S::IntoIter {
            x.into_iter()
        }
        helper::<<Self as HasIter<'t, T, &'t Self>>::Base>(self)
    }
}
5 Likes

Great thanks! That's exactly what I was looking for.