4.1 升级到 0.5

原文链接: https://rust-random.github.io/book/update-0.5.html

0.5 版本相比 0.4 版本有相当显著的变化;因此,阅读以下破坏性更改的说明可能值得。本版本还包含许多优化,下文未详细说明。

Crate

我们有了一个新 crate:rand_core!该 crate 包含一些重要的 trait:RngCore、BlockRngCore、SeedableRng 和 CryptoRng,错误类型,以及两个用于实现的辅助模块:le 和 impls。建议生成器的实现使用 rand_core crate,而其他用户仅使用 rand crate,它重新导出了 rand_core 的大部分内容。

rand_derive crate 由于使用率极低以及 Rand 的弃用而被弃用。

特性

添加了几个新的 Cargo 特性标志:

  • alloc,在没有 std 时使用,允许使用 Box 和 Vec
  • serde1 为部分 PRNG 添加序列化支持
  • log 在少数地方添加日志(主要用于 OsRng 和 JitterRng)

Rng 及其相关 trait(核心 trait)

Rng trait 已拆分为两个 trait:由生成器实现的「后端」RngCore 和实现所有便捷扩展方法的「前端」Rng。

生成器的实现必须改为 impl RngCore。鼓励实现使用 rand_core;rand_core::{le, impls} 模块可能很有用。

不需要实现 Rng 的 Rng 用户不需要做太多更改;通常用户可以忘记 RngCore 而只导入 Rng。用户应优先使用 Rng::gen() 而非 RngCore::next_u32() / next_u64(),使用 Rng::fill(dest) 而非 RngCore::fill_bytes(dest)。

Rng / RngCore 方法

为允许来自可能失败的源(例如 OsRng)的错误处理,添加了新方法 RngCore::try_fill_bytes;例如 EntropyRng 使用此机制在 OsRng 失败时回退到 JitterRng,各种处理器产生更好的错误消息。 与之前一样,其他方法在失败时会 panic,但由于这些通常与算法生成器一起使用且算法生成器通常不会失败,这被认为是适当的折衷。

旧 Rng 中的几个方法已被移除或弃用:

  • next_f32 和 next_f64;生成器不再能实现这些;改用 gen
  • gen_iter;用户可以使用带闭包的标准迭代器:::std::iter::repeat(()).map(|()| rng.gen())
  • gen_ascii_chars;如上使用 repeat 和 rng.sample(Alphanumeric)
  • gen_weighted_bool(n);改用 gen_bool(1.0 / n)

Rng 有几个新方法:

  • sample(distr) 是对任何 Distribution 的 distr.sample(rng) 的快捷方式
  • gen_bool(p) 生成布尔值,为 true 的概率为 p
  • fill 和 try_fill,分别对应 fill_bytes 和 try_fill_bytes(即唯一区别是错误处理);这些可以直接填充整数切片/数组,并提供比 gen() 更好的性能

构造 PRNG

新的随机初始化 PRNG

添加了新 trait:FromEntropy。对于任何支持 SeedableRng 的类型自动实现,提供从新鲜、强熵构造:

1
2
3
4
# extern crate rand_0_5 as rand;
use rand::{ChaChaRng, FromEntropy};

let mut rng = ChaChaRng::from_entropy();

播种 PRNG

SeedableRng trait 已修改,通过关联类型(SeedableRng::Seed)包含种子类型,而非模板参数(SeedableRng<Seed>)。此外,所有 PRNG 现在都从字节数组播种([u8; N],N 为某个固定值)。这允许以前不可能的通用 PRNG 播种处理。

PRNG 不再通过 Rand 支持 / gen() 从其他 PRNG 构造,而是通过 SeedableRng::from_rng,它允许错误处理且刻意明确。

SeedableRng::reseed 已被移除,因为它相对于 from_seed 没有实用价值,且其性能优势值得质疑。

SeedableRng 的实现可能需要将其 Seed 类型更改为字节数组;此限制是为了确保可移植的字节序处理。rand_core::le 中有辅助函数可从字节数组读取 u32 和 u64 值。

基于块的 PRNG

rand_core 有新的辅助 trait BlockRngCore 和实现 BlockRng。这些供一次生成一块随机数据而非字大小值的生成器使用。使用此 trait 和实现有两个优势:提供优化的 RngCore 方法,且 PRNG 可以以非常低的开销与 ReseedingRng 一起使用。

密码学 RNG

添加了新 trait:CryptoRng。这纯粹是标记 trait,用于指示哪些生成器应适合密码学,例如 fn foo<R: Rng + CryptoRng>(rng: &mut R)。无法保证适合密码学用途。

错误处理

添加了新的 Error 类型,专为 no-std 兼容性、简洁性以及我们用途所需的足够灵活性(在可能时携带 cause)而设计:

1
2
3
4
5
6
7
# extern crate rand_0_5 as rand;
# use rand::ErrorKind;
pub struct Error {
    pub kind: ErrorKind,
    pub msg: &'static str,
    // 部分字段省略
}

关联的 ErrorKind 允许将错误广泛分类为永久、意外、瞬态和尚未就绪类型。

以下使用新错误类型:

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

外部生成器

我们有了新生成器 EntropyRng,它包装 OsRng 和 JitterRng(优先使用前者,必要时回退到后者)。这允许通过 SeedableRng::from_rng 轻松构造并带回退,例如 IsaacRng::from_rng(EntropyRng::new())?。这与使用 FromEntropy 等价,除了错误处理。

建议在 OsRng 之上使用 EntropyRng 以避免系统生成器损坏的平台上的错误,但应注意 JitterRng 回退非常慢。

PRNG

伪随机数生成器(即确定性算法生成器)自 0.4 以来有一些变化,现在位于 prng 模块中(旧名称暂时保留以保持兼容性;最终这些生成器可能会放在 rand crate 之外)。

所有 PRNG 现在不实现 Copy,以防止意外复制生成器状态(从而导致生成值重复)。通过 Clone 的显式克隆仍然可用。所有 PRNG 现在都有不打印任何内部状态的自定义 Debug 实现;这有助于避免在日志文件中意外泄露密码学生成器状态。外部 PRNG 实现建议遵循此模式(另见 RngCore 文档)。

SmallRng 已作为包装器添加,目前围绕 XorShiftRng(但可能很快换用另一种算法)。这适用于小状态和快速初始化重要但不需要密码学强度的场景。(实际生成性能因基准测试而异;根据用法,这可能是最快的算法,也可能不是,但始终很快。)

ReseedingRng

ReseedingRng 包装器已显著修改以降低开销。不幸的是,新的 ReseedingRng 与所有 RNG 不兼容,仅与使用 BlockRngCore 的那些兼容。

ChaCha

ChaChaRng::set_counter 方法已被两个新方法 set_word_pos 和 set_stream 替换。必要时,旧方法的行为可以如下模拟:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# extern crate rand_0_5 as rand;
# use rand::prelude::*;
# use rand_chacha_0_1::ChaChaRng;
# fn main() {
let lower = 88293;
let higher = 9300932;

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

// 现在:
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 PRNG

IsaacRng 和 Isaac64Rng PRNG 现在有额外的构造方法:new_from_u64(seed)。64 位状态不足以用于密码学,但可能对模拟和游戏有用。未来可能会被从任何可哈希对象构造任何 PRNG 的方法取代。

HC-128

这是新的密码学生成器,被 eSTREAM 选为「适合广泛采用的流密码」之一。这现在是默认密码学生成器,由 StdRng 和 thread_rng() 使用。

辅助函数/trait

Rand trait 已被弃用。相反,鼓励用户使用 Standard,它是真正的分布并支持与 Rand 相同的采样。Rng::gen() 现在使用 Standard,应与之前完全一样工作。有关如何为用户类型 T 实现 Distribution<T> 的 Standard,请参阅 distributions 模块文档。

weak_rng() 已被弃用;改用 SmallRng::from_entropy()。

分布

Sample 和 IndependentSample trait 已被单一 trait Distribution 替换。这大致等价于 IndependentSample,但 ind_sample 被替换为 sample。对可变分布的支持已被移除;虽然似乎可能有少数真正的用途,但这些用途不够广泛,无法证明存在两个独立 trait 或必须提供对分布对象的可变访问。Sample 和 IndependentSample 仍然可用,但已弃用;它们将在未来版本中移除。

Distribution::sample(以及其他几个函数)现在可以直接在类型擦除(unsized)的 RNG 上调用。

RandSample 已被移除(参见 Rand 弃用和新 Standard 分布)。

Closed01 包装器已被移除,但 OpenClosed01 已添加。

均匀分布

两个新分布可用:

  • Standard 为许多不同类型产生均匀分布的样本,并作为 Rand 的替代品
  • Alphanumeric 从 a-z A-Z 0-9 范围采样 char

范围

Range 分布已大幅调整,并重命名为 Uniform:

  • Uniform::new(low, high) 保留(半开 [low, high))
  • Uniform::new_inclusive(low, high) 已添加,在采样范围中包含 high
  • Uniform::sample_single(low, high, rng) 是从 [low, high) 单次采样的更快变体

Uniform 现在可以为用户定义的类型实现;参见 uniform 模块。

非均匀分布

添加了两个分布:

  • Poisson,建模固定时间间隔内来自恒定速率源的预期事件数(例如核衰变)
  • Binomial,建模固定次数是/否试验的结果

采样方法基于《Numerical Recipes in C》中的方法。

指数分布与正态分布

主要的 Exp 和 Normal 分布未变,但「标准」版本 Exp1 和 StandardNormal 不再是包装类型,而是完整分布。不再写 let Exp1(x) = rng.gen();,而是写 let x = rng.sample(Exp1);。

最后修改 August 23, 2026: 更新 (499855b16)