I was reading this code in one of the 2018 preview discussions (thank you again, @CryZe):
impl<'a> IntoIterator for &'a SegmentHistory {
type Item = &'a (i32, Time);
type IntoIter = Iter<'a, (i32, Time)>;
and thinking, hmm, removing the <'a>
there doesn’t really make much of a difference.
But then the elision rules jumped into mind. Associated types are kinda like return types from traits-as-functions, so you can think of that code like fn IntoIterator<'a>(self: &'a SegmentHistory) -> (&'a (i32, Time), Iter<'a, (i32, Time)>)
. But of course you wouldn’t write it like that, since it could fully elide.
That would make the original example into just
impl IntoIterator for &SegmentHistory {
type Item = &(i32, Time);
type IntoIter = Iter<'_, (i32, Time)>;
To me, at least, that’s beautiful. Way better than just removing the <'a>
, all the 'a
s are gone, though of course the Iter
type still has its '_
as a reminder that there’s a lifetime, like it would when used as a return type. And it’s still clear what it should mean, since there’s only one input lifetime available, the same way it’s clear in the function signature case.
Any traps here that I missed? I’d probably start with just the “there’s only one lifetime in the arguments” elision rule. There might prove to be a good equivalent of the “well, take it from self if there’s a self” rule (since there’s a Self
), but that can wait.
(If you’re looking at the original code, you’ll be noticing that there’s another 'a
I didn’t talk about, but it doesn’t cause a problem because -> Iter<'a, (i32, Time)>
can just be replaced by -> Self::IntoIter
.)