Skip to content

Commit 1db3aa4

Browse files
authored
Replace SeedableRng impl for SmallRng with inherent fns (#1368)
* Simplify redundant doc-link * Remove impl of SeedableRng for SmallRng * Fix SeedableRng::Seed type for StdRng * SmallRng: do not expect thread_rng * Make SmallRng::from_thread_rng infallible * Fix benchmarks (SmallRng does not have from_entropy)
1 parent e9a27a8 commit 1db3aa4

File tree

6 files changed

+55
-60
lines changed

6 files changed

+55
-60
lines changed

benches/generators.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ gen_bytes!(gen_bytes_chacha12, ChaCha12Rng::from_entropy());
5050
gen_bytes!(gen_bytes_chacha20, ChaCha20Rng::from_entropy());
5151
gen_bytes!(gen_bytes_std, StdRng::from_entropy());
5252
#[cfg(feature = "small_rng")]
53-
gen_bytes!(gen_bytes_small, SmallRng::from_entropy());
53+
gen_bytes!(gen_bytes_small, SmallRng::from_thread_rng());
5454
gen_bytes!(gen_bytes_os, OsRng);
5555

5656
macro_rules! gen_uint {
@@ -80,7 +80,7 @@ gen_uint!(gen_u32_chacha12, u32, ChaCha12Rng::from_entropy());
8080
gen_uint!(gen_u32_chacha20, u32, ChaCha20Rng::from_entropy());
8181
gen_uint!(gen_u32_std, u32, StdRng::from_entropy());
8282
#[cfg(feature = "small_rng")]
83-
gen_uint!(gen_u32_small, u32, SmallRng::from_entropy());
83+
gen_uint!(gen_u32_small, u32, SmallRng::from_thread_rng());
8484
gen_uint!(gen_u32_os, u32, OsRng);
8585

8686
gen_uint!(gen_u64_step, u64, StepRng::new(0, 1));
@@ -93,7 +93,7 @@ gen_uint!(gen_u64_chacha12, u64, ChaCha12Rng::from_entropy());
9393
gen_uint!(gen_u64_chacha20, u64, ChaCha20Rng::from_entropy());
9494
gen_uint!(gen_u64_std, u64, StdRng::from_entropy());
9595
#[cfg(feature = "small_rng")]
96-
gen_uint!(gen_u64_small, u64, SmallRng::from_entropy());
96+
gen_uint!(gen_u64_small, u64, SmallRng::from_thread_rng());
9797
gen_uint!(gen_u64_os, u64, OsRng);
9898

9999
macro_rules! init_gen {

benches/uniform.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const N_RESAMPLES: usize = 10_000;
2323
macro_rules! sample {
2424
($R:ty, $T:ty, $U:ty, $g:expr) => {
2525
$g.bench_function(BenchmarkId::new(stringify!($R), "single"), |b| {
26-
let mut rng = <$R>::from_entropy();
26+
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
2727
let x = rng.gen::<$U>();
2828
let bits = (<$T>::BITS / 2);
2929
let mask = (1 as $U).wrapping_neg() >> bits;
@@ -35,7 +35,7 @@ macro_rules! sample {
3535
});
3636

3737
$g.bench_function(BenchmarkId::new(stringify!($R), "distr"), |b| {
38-
let mut rng = <$R>::from_entropy();
38+
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
3939
let x = rng.gen::<$U>();
4040
let bits = (<$T>::BITS / 2);
4141
let mask = (1 as $U).wrapping_neg() >> bits;

benches/uniform_float.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const N_RESAMPLES: usize = 10_000;
2727
macro_rules! single_random {
2828
($R:ty, $T:ty, $g:expr) => {
2929
$g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| {
30-
let mut rng = <$R>::from_entropy();
30+
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
3131
let (mut low, mut high);
3232
loop {
3333
low = <$T>::from_bits(rng.gen());
@@ -63,7 +63,7 @@ fn single_random(c: &mut Criterion) {
6363
macro_rules! distr_random {
6464
($R:ty, $T:ty, $g:expr) => {
6565
$g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| {
66-
let mut rng = <$R>::from_entropy();
66+
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
6767
let dist = loop {
6868
let low = <$T>::from_bits(rng.gen());
6969
let high = <$T>::from_bits(rng.gen());

rand_core/src/block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub trait BlockRngCore {
8080
/// A marker trait used to indicate that an [`RngCore`] implementation is
8181
/// supposed to be cryptographically secure.
8282
///
83-
/// See [`CryptoRng`][crate::CryptoRng] docs for more information.
83+
/// See [`CryptoRng`] docs for more information.
8484
pub trait CryptoBlockRng: BlockRngCore { }
8585

8686
/// A wrapper type implementing [`RngCore`] for some type implementing

src/rngs/small.rs

+45-51
Original file line numberDiff line numberDiff line change
@@ -22,58 +22,20 @@ type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus;
2222
/// Note that depending on the application, [`StdRng`] may be faster on many
2323
/// modern platforms while providing higher-quality randomness. Furthermore,
2424
/// `SmallRng` is **not** a good choice when:
25-
/// - Security against prediction is important. Use [`StdRng`] instead.
26-
/// - Seeds with many zeros are provided. In such cases, it takes `SmallRng`
27-
/// about 10 samples to produce 0 and 1 bits with equal probability. Either
28-
/// provide seeds with an approximately equal number of 0 and 1 (for example
29-
/// by using [`SeedableRng::from_entropy`] or [`SeedableRng::seed_from_u64`]),
30-
/// or use [`StdRng`] instead.
3125
///
32-
/// The algorithm is deterministic but should not be considered reproducible
33-
/// due to dependence on platform and possible replacement in future
34-
/// library versions. For a reproducible generator, use a named PRNG from an
35-
/// external crate, e.g. [rand_xoshiro] or [rand_chacha].
36-
/// Refer also to [The Book](https://rust-random.github.io/book/guide-rngs.html).
26+
/// - Portability is required. Its implementation is not fixed. Use a named
27+
/// generator from an external crate instead, for example [rand_xoshiro] or
28+
/// [rand_chacha]. Refer also to
29+
/// [The Book](https://rust-random.github.io/book/guide-rngs.html).
30+
/// - Security against prediction is important. Use [`StdRng`] instead.
3731
///
3832
/// The PRNG algorithm in `SmallRng` is chosen to be efficient on the current
3933
/// platform, without consideration for cryptography or security. The size of
4034
/// its state is much smaller than [`StdRng`]. The current algorithm is
4135
/// `Xoshiro256PlusPlus` on 64-bit platforms and `Xoshiro128PlusPlus` on 32-bit
4236
/// platforms. Both are also implemented by the [rand_xoshiro] crate.
4337
///
44-
/// # Examples
45-
///
46-
/// Initializing `SmallRng` with a random seed can be done using [`SeedableRng::from_entropy`]:
47-
///
48-
/// ```
49-
/// use rand::{Rng, SeedableRng};
50-
/// use rand::rngs::SmallRng;
51-
///
52-
/// // Create small, cheap to initialize and fast RNG with a random seed.
53-
/// // The randomness is supplied by the operating system.
54-
/// let mut small_rng = SmallRng::from_entropy();
55-
/// # let v: u32 = small_rng.gen();
56-
/// ```
57-
///
58-
/// When initializing a lot of `SmallRng`'s, using [`thread_rng`] can be more
59-
/// efficient:
60-
///
61-
/// ```
62-
/// use rand::{SeedableRng, thread_rng};
63-
/// use rand::rngs::SmallRng;
64-
///
65-
/// // Create a big, expensive to initialize and slower, but unpredictable RNG.
66-
/// // This is cached and done only once per thread.
67-
/// let mut thread_rng = thread_rng();
68-
/// // Create small, cheap to initialize and fast RNGs with random seeds.
69-
/// // One can generally assume this won't fail.
70-
/// let rngs: Vec<SmallRng> = (0..10)
71-
/// .map(|_| SmallRng::from_rng(&mut thread_rng).unwrap())
72-
/// .collect();
73-
/// ```
74-
///
7538
/// [`StdRng`]: crate::rngs::StdRng
76-
/// [`thread_rng`]: crate::thread_rng
7739
/// [rand_chacha]: https://crates.io/crates/rand_chacha
7840
/// [rand_xoshiro]: https://crates.io/crates/rand_xoshiro
7941
#[cfg_attr(doc_cfg, doc(cfg(feature = "small_rng")))]
@@ -102,21 +64,53 @@ impl RngCore for SmallRng {
10264
}
10365
}
10466

105-
impl SeedableRng for SmallRng {
106-
type Seed = <Rng as SeedableRng>::Seed;
107-
67+
impl SmallRng {
68+
/// Construct an instance seeded from another `Rng`
69+
///
70+
/// We recommend that the source (master) RNG uses a different algorithm
71+
/// (i.e. is not `SmallRng`) to avoid correlations between the child PRNGs.
72+
///
73+
/// # Example
74+
/// ```
75+
/// # use rand::rngs::SmallRng;
76+
/// let rng = SmallRng::from_rng(rand::thread_rng());
77+
/// ```
10878
#[inline(always)]
109-
fn from_seed(seed: Self::Seed) -> Self {
110-
SmallRng(Rng::from_seed(seed))
79+
pub fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
80+
Rng::from_rng(rng).map(SmallRng)
11181
}
11282

83+
/// Construct an instance seeded from the thread-local RNG
84+
///
85+
/// # Panics
86+
///
87+
/// This method panics only if [`thread_rng`](crate::thread_rng) fails to
88+
/// initialize.
89+
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
11390
#[inline(always)]
114-
fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
115-
Rng::from_rng(rng).map(SmallRng)
91+
pub fn from_thread_rng() -> Self {
92+
let mut seed = <Rng as SeedableRng>::Seed::default();
93+
crate::thread_rng().fill_bytes(seed.as_mut());
94+
SmallRng(Rng::from_seed(seed))
11695
}
11796

97+
/// Construct an instance from a `u64` seed
98+
///
99+
/// This provides a convenient method of seeding a `SmallRng` from a simple
100+
/// number by use of another algorithm to mutate and expand the input.
101+
/// This is suitable for use with low Hamming Weight numbers like 0 and 1.
102+
///
103+
/// **Warning:** the implementation is deterministic but not portable:
104+
/// output values may differ according to platform and may be changed by a
105+
/// future version of the library.
106+
///
107+
/// # Example
108+
/// ```
109+
/// # use rand::rngs::SmallRng;
110+
/// let rng = SmallRng::seed_from_u64(1);
111+
/// ```
118112
#[inline(always)]
119-
fn seed_from_u64(state: u64) -> Self {
113+
pub fn seed_from_u64(state: u64) -> Self {
120114
SmallRng(Rng::seed_from_u64(state))
121115
}
122116
}

src/rngs/std.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ impl RngCore for StdRng {
5757
}
5858

5959
impl SeedableRng for StdRng {
60-
type Seed = <Rng as SeedableRng>::Seed;
60+
// Fix to 256 bits. Changing this is a breaking change!
61+
type Seed = [u8; 32];
6162

6263
#[inline(always)]
6364
fn from_seed(seed: Self::Seed) -> Self {

0 commit comments

Comments
 (0)