Updating to 0.5

The 0.5 release has quite significant changes over the 0.4 release; as such, it may be worth reading through the following coverage of breaking changes. This release also contains many optimisations, which are not detailed below.

Crates

We have a new crate: rand_core! This crate houses some important traits, RngCore, BlockRngCore, SeedableRng and CryptoRng, the error types, as well as two modules with helpers for implementations: le and impls. It is recommended that implementations of generators use the rand_core crate while other users use only the rand crate, which re-exports most parts of rand_core.

The rand_derive crate has been deprecated due to very low usage and deprecation of Rand.

Features

Several new Cargo feature flags have been added:

  • alloc, used without std, allows use of Box and Vec
  • serde1 adds serialization support to some PRNGs
  • log adds logging in a few places (primarily to OsRng and JitterRng)

Rng and friends (core traits)

Rng trait has been split into two traits, a "back end" RngCore (implemented by generators) and a "front end" Rng implementing all the convenient extension methods.

Implementations of generators must impl RngCore instead. Usage of rand_core for implementations is encouraged; the rand_core::{le, impls} modules may prove useful.

Users of Rng who don't need to implement it won't need to make so many changes; often users can forget about RngCore and only import Rng. Instead of RngCore::next_u32() / next_u64() users should prefer Rng::gen(), and instead of RngCore::fill_bytes(dest), Rng::fill(dest) can be used.

Rng / RngCore methods

To allow error handling from fallible sources (e.g. OsRng), a new RngCore::try_fill_bytes method has been added; for example EntropyRng uses this mechanism to fall back to JitterRng if OsRng fails, and various handlers produce better error messages. As before, the other methods will panic on failure, but since these are usually used with algorithmic generators which are usually infallible, this is considered an appropriate compromise.

A few methods from the old Rng have been removed or deprecated:

  • next_f32 and next_f64; these are no longer implementable by generators; use gen instead
  • gen_iter; users may instead use standard iterators with closures: ::std::iter::repeat(()).map(|()| rng.gen())
  • gen_ascii_chars; use repeat as above and rng.sample(Alphanumeric)
  • gen_weighted_bool(n); use gen_bool(1.0 / n) instead

Rng has a few new methods:

  • sample(distr) is a shortcut for distr.sample(rng) for any Distribution
  • gen_bool(p) generates a boolean with probability p of being true
  • fill and try_fill, corresponding to fill_bytes and try_fill_bytes respectively (i.e. the only difference is error handling); these can fill and integer slice / array directly, and provide better performance than gen()

Constructing PRNGs

New randomly-initialised PRNGs

A new trait has been added: FromEntropy. This is automatically implemented for any type supporting SeedableRng, and provides construction from fresh, strong entropy:


#![allow(unused)]
fn main() {
use rand::{ChaChaRng, FromEntropy};

let mut rng = ChaChaRng::from_entropy();
}

Seeding PRNGs

The SeedableRng trait has been modified to include the seed type via an associated type (SeedableRng::Seed) instead of a template parameter (SeedableRng<Seed>). Additionally, all PRNGs now seed from a byte-array ([u8; N] for some fixed N). This allows generic handling of PRNG seeding which was not previously possible.

PRNGs are no longer constructed from other PRNGs via Rand support / gen(), but through SeedableRng::from_rng, which allows error handling and is intentionally explicit.

SeedableRng::reseed has been removed since it has no utility over from_seed and its performance advantage is questionable.

Implementations of SeedableRng may need to change their Seed type to a byte-array; this restriction has been made to ensure portable handling of Endianness. Helper functions are available in rand_core::le to read u32 and u64 values from byte arrays.

Block-based PRNGs

rand_core has a new helper trait, BlockRngCore, and implementation, BlockRng. These are for use by generators which generate a block of random data at a time instead of word-sized values. Using this trait and implementation has two advantages: optimised RngCore methods are provided, and the PRNG can be used with ReseedingRng with very low overhead.

Cryptographic RNGs

A new trait has been added: CryptoRng. This is purely a marker trait to indicate which generators should be suitable for cryptography, e.g. fn foo<R: Rng + CryptoRng>(rng: &mut R). Suitability for cryptographic use cannot be guaranteed.

Error handling

A new Error type has been added, designed explicitly for no-std compatibility, simplicity, and enough flexibility for our uses (carrying a cause when possible):


#![allow(unused)]
fn main() {
pub struct Error {
    pub kind: ErrorKind,
    pub msg: &'static str,
    // some fields omitted
}
}

The associated ErrorKind allows broad classification of errors into permanent, unexpected, transient and not-yet-ready kinds.

The following use the new error type:

  • RngCore::try_fill_bytes
  • Rng::try_fill
  • OsRng::new
  • JitterRng::new

External generators

We have a new generator, EntropyRng, which wraps OsRng and JitterRng (preferring to use the former, but falling back to the latter if necessary). This allows easy construction with fallback via SeedableRng::from_rng, e.g. IsaacRng::from_rng(EntropyRng::new())?. This is equivalent to using FromEntropy except for error handling.

It is recommended to use EntropyRng over OsRng to avoid errors on platforms with broken system generator, but it should be noted that the JitterRng fallback is very slow.

PRNGs

Pseudo-Random Number Generators (i.e. deterministic algorithmic generators) have had a few changes since 0.4, and are now housed in the prng module (old names remain temporarily available for compatibility; eventually these generators will likely be housed outside the rand crate).

All PRNGs now do not implement Copy to prevent accidental copying of the generator's state (and thus repetitions of generated values). Explicit cloning via Clone is still available. All PRNGs now have a custom implementation of Debug which does not print any internal state; this helps avoid accidentally leaking cryptographic generator state in log files. External PRNG implementations are advised to follow this pattern (see also doc on RngCore).

SmallRng has been added as a wrapper, currently around XorShiftRng (but likely another algorithm soon). This is for uses where small state and fast initialisation are important but cryptographic strength is not required. (Actual performance of generation varies by benchmark; depending on usage this may or may not be the fastest algorithm, but will always be fast.)

ReseedingRng

The ReseedingRng wrapper has been significantly altered to reduce overhead. Unfortunately the new ReseedingRng is not compatible with all RNGs, but only those using BlockRngCore.

ChaCha

The method ChaChaRng::set_counter has been replaced by two new methods, set_word_pos and set_stream. Where necessary, the behaviour of the old method may be emulated as follows:


#![allow(unused)]
fn main() {
use rand::prelude::*;
use rand_chacha::ChaChaRng;
let lower = 88293;
let higher = 9300932;

// previously:
// let mut rng = rand::ChaChaRng::new_unseeded();
// rng.set_counter(lower, higher);

// now:
let mut rng = ChaChaRng::from_seed([0u8; 32]);
rng.set_word_pos(lower << 4);
rng.set_stream(higher);

assert_eq!(4060232610, rng.next_u32());
assert_eq!(2786236710, rng.next_u32());
}

ISAAC PRNGs

The IsaacRng and Isaac64Rng PRNGs now have an additional construction method: new_from_u64(seed). 64 bits of state is insufficient for cryptography but may be of use in simulations and games. This will likely be superseded by a method to construct any PRNG from any hashable object in the future.

HC-128

This is a new cryptographic generator, selected as one of the "stream ciphers suitable for widespread adoption" by eSTREAM. This is now the default cryptographic generator, used by StdRng and thread_rng().

Helper functions/traits

The Rand trait has been deprecated. Instead, users are encouraged to use Standard which is a real distribution and supports the same sampling as Rand. Rng::gen() now uses Standard and should work exactly as before. See the documentation of the distributions module on how to implement Distribution<T> for Standard for user types T

weak_rng() has been deprecated; use SmallRng::from_entropy() instead.

Distributions

The Sample and IndependentSample traits have been replaced by a single trait, Distribution. This is largely equivalent to IndependentSample, but with ind_sample replaced by just sample. Support for mutable distributions has been dropped; although it appears there may be a few genuine uses, these are not used widely enough to justify the existence of two independent traits or of having to provide mutable access to a distribution object. Both Sample and IndependentSample are still available, but deprecated; they will be removed in a future release.

Distribution::sample (as well as several other functions) can now be called directly on type-erased (unsized) RNGs.

RandSample has been removed (see Rand deprecation and new Standard distribution).

The Closed01 wrapper has been removed, but OpenClosed01 has been added.

Uniform distributions

Two new distributions are available:

  • Standard produces uniformly-distributed samples for many different types, and acts as a replacement for Rand
  • Alphanumeric samples chars from the ranges a-z A-Z 0-9

Ranges

The Range distribution has been heavily adapted, and renamed to Uniform:

  • Uniform::new(low, high) remains (half open [low, high))
  • Uniform::new_inclusive(low, high) has been added, including high in the sample range
  • Uniform::sample_single(low, high, rng) is a faster variant for single usage sampling from [low, high)

Uniform can now be implemented for user-defined types; see the uniform module.

Non-uniform distributions

Two distributions have been added:

  • Poisson, modeling the number of events expected from a constant-rate source within a fixed time interval (e.g. nuclear decay)
  • Binomial, modeling the outcome of a fixed number of yes-no trials

The sampling methods are based on those in "Numerical Recipes in C".

Exponential and Normal distributions

The main Exp and Normal distributions are unchanged, however the "standard" versions, Exp1 and StandardNormal are no longer wrapper types, but full distributions. Instead of writing let Exp1(x) = rng.gen(); you now write let x = rng.sample(Exp1);.