Background
What is your motivation?
Provide a way to produce predictable results for (a function similar to) rand::seq::IteratorRandom::choose no matter what type of iterator it is.
Right now the behaviour of rand::seq::IteratorRandom::choose depends on the type of Iterator that is passed in. This is mentioned in the docs as an "optimization" however it isn't clear that this is a behaviour affecting "optimization". Furthermore there is no good alternative function which doesn't have this "optimization".
Example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a62f77f38f4a0b66df57106d0cf6a4a4
extern crate rand; // 0.7.3
extern crate rand_pcg; // 0.2.1
fn choose(i: &mut dyn Iterator<Item=u32>) -> u32 {
let mut rng = rand_pcg::Pcg32::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7);
rand::seq::IteratorRandom::choose(i, &mut rng).unwrap()
}
fn main() {
dbg!(choose(&mut (0..32))); // 5
dbg!(choose(&mut (0..32).filter(|_| true))); // 3
}
What type of application is this? numerical simulation
Feature request
- Clarify the documentation to emphasize that
rand::seq::IteratorRandom::choose has different behaviour depending on the return value of the size_hint method.
- Provide an alternative function that will select the same element (including
Rng state) for any iterator of the same length.
Workarounds
Unify type:
One option to get a consistent result is ensure that you are always working with the same type. For example the following always collects to a Vec.
fn stable_choose_collect<T>(i: impl Iterator<Item=T>, mut rng: impl rand::Rng) -> Option<T> {
rand::seq::IteratorRandom::choose(i.collect::<Vec<_>>().into_iter(), &mut rng)
}
Get rid of size_hint.
It would be more "proper" to make a wrapper type but since rand::seq::IteratorRandom::choose only performs its "optimization" if lower == upper we just need to make those not equal. The easiest way to do this is is call .filter(|_| true) on the iterator. Because it can't tell how many elements you will filter it will set the lower bound to 0.
fn stable_choose_no_hint<T>(i: impl Iterator<Item=T>, mut rng: impl rand::Rng) -> Option<T> {
rand::seq::IteratorRandom::choose(i.filter(|_| true), &mut rng)
}
Background
What is your motivation?
Provide a way to produce predictable results for (a function similar to)
rand::seq::IteratorRandom::chooseno matter what type of iterator it is.Right now the behaviour of
rand::seq::IteratorRandom::choosedepends on the type of Iterator that is passed in. This is mentioned in the docs as an "optimization" however it isn't clear that this is a behaviour affecting "optimization". Furthermore there is no good alternative function which doesn't have this "optimization".Example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a62f77f38f4a0b66df57106d0cf6a4a4
What type of application is this? numerical simulation
Feature request
rand::seq::IteratorRandom::choosehas different behaviour depending on the return value of thesize_hintmethod.Rngstate) for any iterator of the same length.Workarounds
Unify type:
One option to get a consistent result is ensure that you are always working with the same type. For example the following always collects to a
Vec.Get rid of size_hint.
It would be more "proper" to make a wrapper type but since
rand::seq::IteratorRandom::chooseonly performs its "optimization" iflower == upperwe just need to make those not equal. The easiest way to do this is is call.filter(|_| true)on the iterator. Because it can't tell how many elements you will filter it will set the lower bound to0.