Defining and implementing the Iterable trait with GATs
🚨 Warning: Speculation ahead. This is the "shiny future" page that integrates various speculative features. To see how things work today, see the corresponding page on the explainer.
To express traits like Iterable, we can make use generic associated types -- that is, associated types with generic parameters. Here is the complete Iterable trait:
#![allow(unused)] fn main() { trait Iterable { // Type of item yielded up; will be a reference into `Self`. type Item<'collection>; // Type of iterator we return. Will return `Self::Item` elements. type Iterator<'collection>: Iterator<Item = Self::Item<'collection>>; fn iter(&self) -> Self::Iterator<'_>; // ^ ^^ // // Returns a `Self::Iter` derived from `self`. } }
Let's walk through it piece by piece...
- We added a
'collectionparameter toItem. This represents "the specific collection that theItemis borrowed from" (or, if you prefer, the lifetime for which that collection is borrowed). - The same
'collectionparameter is added toIterator, indicating the collection that the iterator borrows its items from. - In the
itermethod, the value of'collectioncomes fromself, indicating thatiterreturns anIteratorlinked toself.
Implementing the trait
Let's write an implementation of this trait. We'll implement it for the Vec<T> type; a &Vec<T> can be coerced into a &[T] slice, so we can re-use the slice Iter that we defined before (the [playground] link includes an impl of Iterable for [T] as well, but we'll use Vec here because it's more convenient).
#![allow(unused)] fn main() { // from before struct Iter<'c, T> { data: &'c [T], } impl<T> Iterable for Vec<T> { type Item<'c> = &'c T; type Iterator<'c> = Iter<'c, T>; fn iter(&self) -> Self::Iterator<'_> { Iter { data: self } } } }
Invoking it
Now that we have the Iterable trait, we can reference it in our "count twice" function.
#![allow(unused)] fn main() { fn count_twice<I: Iterable>(collection: &I) { let mut count = 0; for _ in collection.iter() { count += 1; } for elem in collection.iter() { process(elem, count); } } }
and we can invoke that by writing code like count_twice(&vec![1, 2, 3, 4, 5, 6]).
[Play with the code from this section on the Rust playground.][playground]