I have a pattern in my code that occurs often enough that I'd like to create a reusable abstraction for it. Basically my GameState
struct can contain a random number generator:
struct GameState {
pub rng: Option<StdRng>
}
and functions for building iterators of various kinds:
fn legal_actions::evaluate<'a>(game: &'a GameState) -> Box<dyn Iterator<Item = UserAction> + 'a>;
If this generator is present, we use it for random operations, otherwise we use the standard thread_rng()
. The way this usually works is as follows:
fn random_action(game: &mut GameState) -> Option<UserAction> {
if game.rng.is_some() {
let actions = legal_actions::evaluate(&game).collect::<Vec<_>>();
actions.into_iter().choose(game.rng.as_mut()?)
} else {
legal_actions::evaluate(&game).choose(&mut rand::thread_rng())
}
}
In the default case we don't need to .collect()
anything because there's no conflict between mutable and immutable borrowing, which is a nice performance optimization.
I'm trying to abstract this out in a reusable pattern, but I've had a really hard time getting the lifetimes right. I think the obvious starting point would be something like:
fn choose_randomly<It>(
game: &mut GameState,
generator: impl FnOnce(&GameState) -> It,
) -> Option<It::Item>
where
It: Iterator,
{
if game.rng.is_some() {
let actions = generator(game).collect::<Vec<_>>();
actions.into_iter().choose(game.rng.as_mut()?)
} else {
generator(game).choose(&mut rand::thread_rng())
}
}
but I haven't been able to get something like this to work because of various lifetime issues:
error: lifetime may not live long enough
choose_randomly(&mut game, |g| legal_actions::evaluate(&g))
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| return type of closure is Box<(dyn Iterator<Item = UserAction> + '2)>
| has type `&'1 GameState`
Is there a way to create a generalized version of this function?