So, it's quite common in Python to pass around generators. Like
class MyClass:
def give_something(self):
# this is like returning iterator in Rust
return (i * 2 for i in self.data)
myobj = MyClass(some_data)
for item in myobj.give_something():
print(item)
Unfortunately, in Rust I banged my head against lifetime issues and had to return Vecs, paying the allocation costs.
Finally, my brain figured this out: lifetime should be on the returned iterator generic type, AND inside the map/filter callbacks!
pub fn find_derivatives<'a>(&'a self, key: &'a Key) -> impl Iterator<Item = Val> + 'a {
// in my real-life case, &Key actually didn't need 'a
self.data.iter()
// (I'm surprised it's && here, not &)
.filter(|(k, _): &&'a (Key, Val)| *k == *key)
.map(|(_, v): &'a (Key, Val)| Val(v.0 * 2))
}
}
There's a double reference here because filter is generic and designed to work with any Item type— It always adds a level of reference so that the item can be later yielded from the iterator if it passes the filter. When the iterator item type is itself a reference, this results in two &s.
The official books and documentation lags a bit, sadly. Also there are some rough edges about how impl Trait captures, or doesn't capture, lifetimes. And how that happens is going to change in the next edition to boot.
For example, if this was in a trait, the impl Trait would have silently captured your input lifetimes, and you could have just written
The downsides are that it's much less apparent that the return value is still holding a borrow of all the inputs, and there will be cases of overcapture (holding on to borrows you don't need to) instead of undercapture (not allowing holding on to borrows you need to).
I have a very short blurb here, and one could probably get from the last example to your solution. But what we really deserve is a writeup of all the RPIT-like language features (including the planned type alias).
(n.b. + 'a is perfectly fine for your use case; the main downside is that it imposes an outlives bound on everything else, including captured type generics, but those downsides don't apply to your code.)
Yes, it's just this playground example that I had to add 'a to the input. I have stepped on that rake too, half a year ago -- removing explicit lifetime fixed an issue, and it was quite an epiphany, and probably gave more gut feeling of how lifetimes work.