-
-
Notifications
You must be signed in to change notification settings - Fork 486
Description
Distribution::sample_iter and the DistIter type currently have signature:
fn sample_iter<'a, R>(&'a self, rng: &'a mut R) -> DistIter<'a, Self, R, T> { ... }
struct DistIter<'a, D: 'a, R: 'a, T> { ... }The lifetimes and borrowing mean that it is impossible to construct a distribution or RNG inside a function and return the sample_iter, e.g. if one is trying to abstract some iterator pipeline
fn ten_dice_rolls_other_than_five<'a, R: Rng>(rng: &'a mut R) -> impl Iterator<i32> + 'a {
Uniform::new_inclusive(1, 6)
.sample_iter(rng)
.filter(|x| *x != 5)
.take(10)
}However, these lifetimes aren't entirely necessary, because of the impls of Distribution for &D and RngCore for &mut R:
impl<'a, T, D: Distribution<T>> Distribution<T> for &'a D
impl<'a, R> RngCore for &'a mut R where R: RngCore + ?Sized(Side note: the Distribution impl could use D: ?Sized like RngCore, to allow &Distribution<T> trait objects to impl Distribution<T>.)
These impls mean that the signature for sample_iter and definition of DistIter could be simplified to:
fn sample_iter<R>(self, rng: R) -> DistIter<Self, R, T> { ... }
struct DistIter<D, R, T> { ... }The impls above specifically mean that if someone wants to pass a borrowed rng they still can, and similarly for a borrowed distribution. This also applies to Rng::sample_iter (both parameters should become owned).
See https://play.rust-lang.org/?gist=c050e777ed23fb40e320ba0eb340d031&version=stable&mode=debug&edition=2015 for a playground demonstrating the problem with the old API and how the new API fixes it without breaking (much) code.
It also shows that a by_ref function similar to Iterator::by_ref (and Read and Write) may be useful with this sort of API, to use as a postfix &/&mut:
trait RngCore {
...
fn by_ref(&mut self) -> &mut Self { self }
}
trait Distribution<T> {
...
fn by_ref(&self) -> &Self { self }
}