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.
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)
}
}