4.4 升级到 0.8
5 分钟阅读
下文提供了将代码从 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 被复制的代码必须更新为使用可变引用。例如,
| |
可以替换为以下代码:
| |
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 你可以写:使用 Rand 0.8,等价于以下代码: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();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(从randcrate 移入) - 添加了
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中的自定义Floattrait 已被num_traits::Float替换。用户定义类型的任何Float实现都必须迁移。得益于num_traits::Float的数学函数,rand_distr现在支持no_std。
此外,有一些较小的改进:
- 改进了
WeightedIndex分布的舍入错误和 NaN 处理。 rand_distr::Exp分布现在支持lambda = 0参数化。
序列
现在支持无放回加权采样,参见 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),帮助执行我们关于值破坏性更改的规则(参见[可复现性]部分)。