GAT and lifetime bounds

I was trying to write a trait for iterators like vec::Drain and vec::IntoIter where they offer an as_slice method, so are "previewable" to iterate returning references nondestructively before iterating returning values later. (Yes, I could just make an AsSlice trait, but ideally this would also work for by-ref combinators of these types like iter::Filter.)

However, I ran into an issue when describing this trait using GAT: I need a root generic type to outlive the GAT lifetime.

trait PreviewableIterator: Iterator {
    type Preview<'a>: Iterator<Item=&'a Self::Item>;
    fn preview(&self) -> Self::Preview<'_>;
}

impl<T> PreviewableIterator for vec::Drain<'_, T> {
    type Preview<'a> = slice::Iter<'a, T>;
    fn preview(&self) -> slice::Iter<'_, T> {
        self.as_slice().iter()
    }
}

impl<T> PreviewableIterator for vec::IntoIter<T> {
    type Preview<'a> = slice::Iter<'a, T>;
    fn preview(&self) -> slice::Iter<'_, T> {
        self.as_slice().iter()
    }
}

This feels like a use case that should be servable by GAT, but I don't know how we might even make this bound expressible except with T: 'static (overly restrictive) or exclusively via implied bounds (really scarily implicit).

1 Like

Something like this?

#![feature(vec_drain_as_slice)]
#![feature(generic_associated_types)]
use std::{slice, vec};

trait PreviewableIterator: Iterator {
    type Preview<'a>: Iterator<Item=&'a Self::Item> where Self: 'a;
    fn preview(&self) -> Self::Preview<'_>;
}

impl<T> PreviewableIterator for vec::Drain<'_, T> {
    type Preview<'a> where Self: 'a = slice::Iter<'a, T>;
    fn preview(&self) -> slice::Iter<'_, T> {
        self.as_slice().iter()
    }
}

impl<T> PreviewableIterator for vec::IntoIter<T> {
    type Preview<'a> where Self: 'a = slice::Iter<'a, T>;
    fn preview(&self) -> slice::Iter<'_, T> {
        self.as_slice().iter()
    }
}

(Edit: In case I wasn’t clear, the above code compiles today.)

2 Likes

The idea would be to add a where clause either on the preview method:

fn preview<'a>(&'a self) -> Self::Preview<'a> where Self::Preview<'a>: 'a;

or to the associated type itself (not possible yet but included in the RFC):

type Preview<'a>: Iterator<Item=&'a Self::Item> where Self::Preview<'a>: 'a;

Now, the caller of preview needs to prove that the lifetime bound holds, either by propagating the lifetime bound (possibility with a higher ranked lifetime bound), or in presence of a concrete type to directly prove that the bound holds: in your case proving that T: 'a holds would be sufficient.

The writers of the RFC were aware of this use case / problem, although it’s only mentioned very briefly:

@nikomatsakis believes that where clauses will be needed on associated type constructors specifically to handle lifetime well formedness in some cases. The exact details are left ouf of this RFC because they will emerge more fully during implementation.

See https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md#evaluating-bounds-and-where-clauses

1 Like