-
-
Notifications
You must be signed in to change notification settings - Fork 463
Deprecate random
and weak_rng
, add SmallRng
#296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0396bc5
1f0df3c
3119279
075593f
887efe1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,12 +10,11 @@ | |
|
||
//! Utilities for random number generation | ||
//! | ||
//! The key functions are `random()` and `Rng::gen()`. These are polymorphic and | ||
//! so can be used to generate any type supporting the [`Uniform`] distribution | ||
//! (i.e. `T` where `Uniform`: `Distribution<T>`). Type inference | ||
//! means that often a simple call to `rand::random()` or `rng.gen()` will | ||
//! suffice, but sometimes an annotation is required, e.g. | ||
//! `rand::random::<f64>()`. | ||
//! The key function is `Rng::gen()`. It is polymorphic and so can be used to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably wants a bit more re-writing, but this doesn't have to be done now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think we should advertise |
||
//! generate any type supporting the [`Uniform`] distribution (i.e. `T` where | ||
//! `Uniform`: `Distribution<T>`). Type inference means that often a simple call | ||
//! to `rng.gen()` will suffice, but sometimes an annotation is required, e.g. | ||
//! `rng.gen::<f64>()`. | ||
//! | ||
//! See the `distributions` submodule for sampling random numbers from | ||
//! distributions like normal and exponential. | ||
|
@@ -88,11 +87,6 @@ | |
//! } | ||
//! ``` | ||
//! | ||
//! ```rust | ||
//! let tuple = rand::random::<(f64, char)>(); | ||
//! println!("{:?}", tuple) | ||
//! ``` | ||
//! | ||
//! ## Monte Carlo estimation of π | ||
//! | ||
//! For this example, imagine we have a square with sides of length 2 and a unit | ||
|
@@ -289,7 +283,8 @@ pub use error::{ErrorKind, Error}; | |
|
||
// convenience and derived rngs | ||
#[cfg(feature="std")] pub use entropy_rng::EntropyRng; | ||
#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng, random}; | ||
#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng}; | ||
#[cfg(feature="std")] #[allow(deprecated)] pub use thread_rng::random; | ||
|
||
use distributions::{Distribution, Uniform, Range}; | ||
use distributions::range::SampleRange; | ||
|
@@ -1091,6 +1086,81 @@ impl SeedableRng for StdRng { | |
} | ||
} | ||
|
||
/// An RNG recommended when small state, cheap initialization and good | ||
/// performance are required. The PRNG algorithm in `SmallRng` is choosen to be | ||
/// efficient on the current platform, **without consideration for cryptography | ||
/// or security**. The size of its state is much smaller than for `StdRng`. | ||
/// | ||
/// Reproducibility of output from this generator is however not required, thus | ||
/// future library versions may use a different internal generator with | ||
/// different output. Further, this generator may not be portable and can | ||
/// produce different output depending on the architecture. If you require | ||
/// reproducible output, use a named RNG, for example `XorShiftRng`. | ||
/// | ||
/// The current algorithm used on all platforms is [Xorshift]. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Initializing `StdRng` with a random seed can be done using `NewRng`: | ||
/// | ||
/// ``` | ||
/// use rand::{NewRng, SmallRng}; | ||
/// | ||
/// // Create small, cheap to initialize and fast RNG with a random seed. | ||
/// // The randomness is supplied by the operating system. | ||
/// let mut small_rng = SmallRng::new().unwrap(); | ||
/// ``` | ||
/// | ||
/// When initializing a lot of `SmallRng`, using `thread_rng` can be more | ||
/// efficient: | ||
/// | ||
/// ``` | ||
/// use rand::{SeedableRng, SmallRng, thread_rng}; | ||
/// | ||
/// // Create a big, expensive to initialize and slower, but unpredictable RNG. | ||
/// // This is cached and done only once per thread. | ||
/// let mut thread_rng = thread_rng(); | ||
/// // Create small, cheap to initialize and fast RNG with a random seed. | ||
/// // This is very unlikely to fail. | ||
/// let mut small_rng = SmallRng::from_rng(&mut thread_rng).unwrap(); | ||
/// ``` | ||
/// | ||
/// [Xorshift]: struct.XorShiftRng.html | ||
#[derive(Clone, Debug)] | ||
pub struct SmallRng(XorShiftRng); | ||
|
||
impl RngCore for SmallRng { | ||
fn next_u32(&mut self) -> u32 { | ||
self.0.next_u32() | ||
} | ||
|
||
fn next_u64(&mut self) -> u64 { | ||
self.0.next_u64() | ||
} | ||
|
||
fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
self.0.fill_bytes(dest); | ||
} | ||
|
||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
self.0.try_fill_bytes(dest) | ||
} | ||
} | ||
|
||
impl SeedableRng for SmallRng { | ||
type Seed = <XorShiftRng as SeedableRng>::Seed; | ||
|
||
fn from_seed(seed: Self::Seed) -> Self { | ||
SmallRng(XorShiftRng::from_seed(seed)) | ||
} | ||
|
||
fn from_rng<R: Rng>(rng: &mut R) -> Result<Self, Error> { | ||
XorShiftRng::from_rng(rng).map(|rng| SmallRng(rng)) | ||
} | ||
} | ||
|
||
/// DEPRECATED: use `SmallRng` instead. | ||
/// | ||
/// Create a weak random number generator with a default algorithm and seed. | ||
/// | ||
/// It returns the fastest `Rng` algorithm currently available in Rust without | ||
|
@@ -1099,13 +1169,13 @@ impl SeedableRng for StdRng { | |
/// create the `Rng` yourself. | ||
/// | ||
/// This will seed the generator with randomness from thread_rng. | ||
#[deprecated(since="0.5.0", note="removed in favor of SmallRng")] | ||
#[cfg(feature="std")] | ||
pub fn weak_rng() -> XorShiftRng { | ||
XorShiftRng::from_rng(&mut thread_rng()).unwrap_or_else(|err| | ||
panic!("weak_rng failed: {:?}", err)) | ||
} | ||
|
||
|
||
/// DEPRECATED: use `seq::sample_iter` instead. | ||
/// | ||
/// Randomly sample up to `amount` elements from a finite iterator. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really not very ergonomic; we've gone from 1 identifier to 4 with lots of extra syntax too. Any better ideas?
Error handling is pedantic because
thread_rng
will almost never fail, but we can't really be removed fromfrom_rng
.We could allow something like
thread_rng().new_rng::<SmallRng>()
but it's not a lot better and adds a function toRng
which doesn't have many other uses.We could just rename
weak_rng
tosmall_rng
. Not sure.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it's an issue outside of small examples. Usually you want to reuse
thread_rng()
. Maybe something like the following makes it more clear in the examples:This is even more verbose, but it teaches the correct pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also wrong:
thread_rng()
is only slow on the first call per thread.SmallRng::new()
would be faster ifthread_rng
was never otherwise used — but chances are good that it would be used somewhere, I guess (at least oncestd
's hash function randomisation uses this, not its own version).Yes, copy-pasting this is fine, but often people don't look up the doc, they just vaguely remember how to do something and think, ah,
SmallRng::new()
should work.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I was trying to say with "This automatically done only once per thread.", but I guess it could be said more clearly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could implement
NewRng
forSmallRng
such that it usesthread_rng()
. However, this requires getting rid of the impl forSeedableRng
or specialization, which is not stable yet.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using
NewSeeded
everywhere is fine. It is also how things used to be until a few months ago. But it would be nice to mention thethread_rng()
method in the documentation just to point out a potential (small) performance advantage. How often will initializing the RNG be a bottleneck?And I am still not sure
thread_rng()
is used all that much always. There isstd::collections::HashMap
that may also use it in the future, and maybe a crates dependency. If it is otherwise not used, spinning upthread_rng
for one initialization ofSmallRng
is wasteful.