6 private links
Because everyone likes to use that LendingIterator example I would offer a bit non-trivial example, maybe it would help to show you that GATs lie in the middle between normal generics and higher kinded types which you may have in C++ or Haskell.
The first observation is that many functions make sense and are, actually, implemented for various containers. E.g. map is implemented for Option and map is implemented for Result but they can not be implemented in common trait before GATs because they need to abstract over type of generic: one of them returns Option<U> and the other one returns Result<U>.
What you really want is to use Option or Result as type, generic argument… but you can't. Name of generic type in not a type in Rust!
GATs alllow you to express what you want in a slightly roundabout way.
You can declare trait Mappable like this:
trait Mappable {
type Item;
type Result<U>;
fn map<U, P: FnMut(Self::Item) -> U>(self, f: P) -> Self::Result<U>;
}
And then implement it like this:
impl<T> Mappable for Option<T> {
type Item = T;
type Result<U> = Option<U>;
fn map<U, P: FnMut(Self::Item) -> U>(self, f: P) -> Option<U> {
self.map(f)
}
}
impl<T, E> Mappable for Result<T, E> {
type Item = T;
type Result<U> = Result<U, E>;
fn map<U, P: FnMut(Self::Item) -> U>(self, f: P) -> Result<U, E> {
self.map(f)
}
}
And then, finally, you get the ability to implement function which is generic over generic like this:
fn zero_to_42<C: Mappable<Item = i32>>(c: C) -> <C as Mappable>::Result<i32> {
c.map(|x| if x == 0 { 42 } else { x })
}
This functions kind of “unpacks then type”, works with it's internals and then “packs everything correctly”.
And you can expand it to work with vector, too
It's similar to how you can deal with functions which are generic-over-pointer-types, but kinda more expansive, it should, hopefully, be obvious that GATs cover many more cases than just simple Arc or Rc.
GATs are very useful even without lifetime GATs (in fact lifetime gats are pretty complicated right now).