4.4 升级到 0.8

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

下文提供了将代码从 rand 0.7 和 rand_distr 0.2 迁移到 rand 0.8 和 rand_distr 0.3 的说明。

依赖

Rand crate 现在需要 rustc 版本 1.36.0 或更高。 这使我们能够移除一些 unsafe 代码并简化内部 cfg 逻辑。

对 getrandom 的依赖已升级到版本 0.2。虽然这不影响 Rand 的 API,但如果你仅将 getrandom 用作依赖,你可能受到一些破坏性更改的影响:

  • 你可能需要更新正在使用的 getrandom 特性。现在可用以下特性:
    • "rdrand":在 no_std 的 x86/x86_64 目标上使用 RDRAND 指令。
    • "js":在 wasm32-unknown-unknown 上使用 JavaScript 调用。这取代了已移除的 stdweb 和 wasm-bindgen 特性。
    • "custom":允许你指定自定义实现。
  • 不支持的目标不再编译。如果你需要之前的行为(运行时 panic 而非编译失败),可以使用 custom 特性提供会 panic 的实现。
  • 从 getrandom 版本 0.2.1 起,Windows XP 和 stdweb 不再受支持。如果你需要支持这两个平台之一,可以添加 getrandom = "=0.2.0" 依赖来固定此版本。
  • Hermit、L4Re 和 UEFI 不再正式支持。你可以在这些平台上使用 rdrand 特性。
  • 最低支持的 Linux 内核版本现在是 2.6.32。

如果你直接使用 getrandom 的 API,可能有进一步的破坏性更改影响你。参见其 changelog。

Serde 已作为可选依赖重新添加(使用 serde1 特性标志),在适当的地方支持许多类型。StdRng 和 SmallRng 被刻意排除,因为这些类型不可移植。

核心特性

ThreadRng

ThreadRng 不再实现 Copy。这是修复其线程局部析构函数中可能的 use-after-free 所必需的。任何依赖 ThreadRng 被复制的代码必须更新为使用可变引用。例如,

1
2
3
4
# use rand_0_7::distributions::{Distribution, Standard};
let rng = rand_0_7::thread_rng();
let a: u32 = Standard.sample_iter(rng).next().unwrap();
let b: u32 = Standard.sample_iter(rng).next().unwrap();

可以替换为以下代码:

1
2
3
4
5
6
7
8
# extern crate rand_0_8 as rand;
# use rand::prelude::*;
# use rand::distributions::Standard;
# fn main () {
let mut rng = thread_rng();
let a: u32 = Standard.sample_iter(&mut rng).next().unwrap();
let b: u32 = Standard.sample_iter(&mut rng).next().unwrap();
# }

gen_range

Rng::gen_range 现在接受 Range 而非两个数字。因此,将 gen_range(a, b) 替换为 gen_range(a..b)。我们建议使用以下正则表达式在所有文件中搜索替换:

  • 将 gen_range\(([^,]*),\s*([^)]*)\) 替换为
  • gen_range(\1..\2)
  • 或 gen_range($1..$2)(如果你的工具不支持反向引用)

大多数 IDE 支持跨文件搜索替换或类似功能;或者可以使用外部工具如 Regexxer。

此更改还有其他几个影响:

  • 现在支持包含范围,例如 gen_range(1..=6) 或 gen_range('A'..='Z')
  • 可能需要显式解引用某些参数
  • 不再支持 SIMD 类型(Uniform 类型仍可直接使用)

fill

AsByteSliceMut trait 已被 Fill trait 替换。这应该只影响在用户定义类型上实现 AsByteSliceMut 的代码,因为 Rng::fill 和 Rng::try_fill 仍支持此前支持的类型。

Fill 支持一些 AsByteSliceMut 无法支持的额外切片类型:[bool]、[char]、[f32]、[f64]。

adapter

整个 rand::rngs::adapter 模块现在限制在 std 特性下。虽然这在技术上是一次破坏性更改,但它应该只影响使用 ReseedingRng 的 no_std 代码,这在实际中不太可能存在。

生成器

StdRng 已从 20 轮 ChaCha20 切换为 ChaCha12 以提高性能。这是复杂度的降低,但 12 轮变体仍被认为安全:参见 rand#932。这对 StdRng 是值破坏性更改。

SmallRng 现在在 32 位和 64 位平台上分别使用 Xoshiro128++ 和 Xoshiro256++ 算法。这减少了来自相似种子的随机数据的相关性并提高了性能。这是值破坏性更改。

我们现在为 StdRng、SmallRng 和 StepRng 实现了 PartialEq 和 Eq。

分布

rand 分布发生了几个较小的更改:

  • Uniform 分布现在额外支持 char 类型,因此例如 rng.gen_range('a'..='f') 现在受支持。
  • 添加了 UniformSampler::sample_single_inclusive。
  • Alphanumeric 分布现在采样字节而非字符。这更贴近内部使用的类型,但旧代码可能需要适配以执行从 u8 到 char 的转换。例如,使用 Rand 0.7 你可以写:
    1
    2
    3
    4
    5
    6
    
    # use rand_0_7::{distributions::Alphanumeric, Rng};
    # let mut rng = rand_0_7::thread_rng();
    let chars: String = std::iter::repeat(())
        .map(|()| rng.sample(Alphanumeric))
        .take(7)
        .collect();
    
    使用 Rand 0.8,等价于以下代码:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    # extern crate rand_0_8 as rand;
    # use rand::{distributions::Alphanumeric, Rng};
    # fn main() {
    # let mut rng = rand::thread_rng();
    let chars: String = std::iter::repeat(())
        .map(|()| rng.sample(Alphanumeric))
        .map(char::from)
        .take(7)
        .collect();
    println!("chars = \"{chars}\"");
    # }
    
  • 使用别名方法的 WeightedIndex 替代实现已从 rand 移至 rand_distr::weighted_alias::WeightedAliasIndex。别名方法对于大尺寸更快,但初始化较慢,使其通用性较低。

在 rand_distr v0.4 中,发生了更多更改(自 v0.2 以来):

  • 添加了 rand_distr::weighted_alias::WeightedAliasIndex(从 rand crate 移入)
  • 添加了 rand_distr::InverseGaussian 和 rand_distr::NormalInverseGaussian
  • 现在支持 Geometric 和 Hypergeometric 分布。
  • Beta 分布使用了不同的算法,提高了性能和精度。这是值破坏性更改。
  • Normal 和 LogNormal 分布现在支持 from_mean_cv 构造方法和 from_zscore 采样方法。
  • rand_distr::Dirichlet 现在在内部使用 boxed 切片而非 Vec。因此,权重作为切片而非 Vec 输入。例如,以下 rand_distr 0.2 代码
    1
    2
    
    # use rand_distr_0_2::Dirichlet;
    Dirichlet::new(vec![1.0, 2.0, 3.0]).unwrap();
    
    可以替换为以下 rand_distr 0.3 代码:
    1
    2
    
    # use rand_distr_0_4::Dirichlet;
    Dirichlet::new(&[1.0, 2.0, 3.0]).unwrap();
    
  • rand_distr::Poisson 不再支持直接采样 u64 值。旧代码可能需要更新以显式执行从 f64 的转换。
  • rand_distr 中的自定义 Float trait 已被 num_traits::Float 替换。用户定义类型的任何 Float 实现都必须迁移。得益于 num_traits::Float 的数学函数,rand_distr 现在支持 no_std。

此外,有一些较小的改进:

序列

现在支持无放回加权采样,参见 rand::seq::index::sample_weighted 和 SliceRandom::choose_multiple_weighted。

IteratorRandom::choose 发生了值破坏性更改,提高了精度和性能。此外,添加了 IteratorRandom::choose_stable 以提供牺牲性能换取与迭代器大小提示无关的替代方案。

特性标志

StdRng 现在由新特性标志 std_rng 控制。默认启用。

nightly 特性不再隐含 simd_support 特性。如果你依赖此来获得 SIMD 支持,必须直接使用 simd_support 特性。

测试

为所有分布添加了值稳定性测试(rand#786),帮助执行我们关于值破坏性更改的规则(参见[可复现性]部分)。

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