The problem is that here:
fn choose_randomly<It>(
game: &mut GameState,
generator: impl FnOnce(&GameState) -> It,
) -> Option<It::Item>
where
It: Iterator,
It
must be a single type, and generator
must produce that type for any input lifetime on &'lifetime GameState
. If It
captures the lifetime, it's a different type for every lifetime, and thus cannot meet this bound.
This is a case where the sugar of Fn
traits becomes salt, because you can't not name the output type when using them. You're going to have to introduce your own redirection to be able to have the result vary in lifetime but still be arbitrarily short.
Here's one way. I rushed it, so maybe it could be a little cleaner. I also put a 'static
bound on the eventual item, as otherwise you can't borrow &mut game
for both the iterator and the RNG at the same time.
The hrtb
is because Rust is bad at inferring higher-lifetime bounds of closures when you need them, so it needs a little assistance in this case.
Edit: Cleaned up a bit.
Other details I rushed over:
- Type parameters like
It
must monomorphize to a single type. -
&'lifetime Thing
is a single type, but there is no single&Thing
type that covers references of any lifetimes (i.e. there is no higher-ranked reference type). When you see&Thing
in type position, it's either part of a higher-ranked type that operates over references of different lifetimes, or it's a&'lifetime Thing
where'lifetime
is singular and inferred. -
impl FnOnce(&GameState) -> It
is short forimpl for<'any> FnOnce(&'any GameState) -> It
; this one is a higher-ranked type as a whole. ButIt
here must be a singular type (i.e. the same for all lifetimes of'any
). - If you know the output type except for some lifetime parameter and single-type generics -- like if the output is always a
Box<dyn Iterator<Item = Item> + '_>
-- then you can use lifetime elision on stable. - Similarly, if you could use
-> (impl Iterator<Item = Item> + '_)
, that would also work, but you can't yet