Iterable, lending iterators, etc
Summary
Traits often contain methods that return data borrowed from self
or some other argument. When the type of data is an associated type, it needs to include a lifetime that links with self
from a calling method. For example, in the Iterable
trait...
#![allow(unused)] fn main() { trait Iterable { type Item<'me> where Self: 'me; type Iter<'me>: Iterator<Item = Self::Item<'me>> where Self: 'me; fn iter(&iter self) -> Self::Item<'_>; } }
...the Item
and Iter
traits take a 'me
parameter, which is linked to the self
variable given when iter
is called.
Details
There are many variants on this pattern:
Iterable
, as shown above;LendingIterator
(and otherLendingFoo
) traits, which permit one to iterate over items but where the data may be stored within the iterator itself;- etc.
The where Self: 'me
shown in the summary is (hopefully) a temporary limitation imposed by the current MVP. It indicates that the 'me
lifetime can be used to borrow data from Self
. Currently these where clauses are mandatory; they may be defaulted or made optional in the future. For a deeper explanation, see the required bounds page in the explainer.
Workarounds
Lacking this pattern, there are a number of common workarounds, each with downsides:
- Use
Box<dyn>
values, as in graphene, though this adds dynamic dispatch overhead, inhibits inlining, and makes interactions withSend
andSync
more complex; - Return a collection, like a
Vec
, as in metamolectular, though this results in unnecessary memory allocation; - Use HRTB, as rustc does, which is complex and leaks into your caller's signatures.