5.1.4. 生成自定义类型随机值

rand-badge cat-science-badge

问题:

你想取得一个随机值,但随机值类型并非标量,而是自定义类型。

解决方案:

假设我们需要随机生成一个元组 (i32, bool, f64),和一个用户定义类型为 Point 的变量。对于随即元组 (i32, bool, f64),如同前几个小结生成标量随机值一样,可以通过 rng.gen::<(i32, bool, f64)>() 直接生成。

而对于随机生成用户定义类型 Point 的变量,则需要使用通用的随机值分布结构体。rand crate 中,定义了一个结构体 Standard,它是通用的随机值分布结构体,通常生成均匀分布的数值,并且具有与类型相适应的范围。我们已经了解,Distribution 是用于创建概率分布类型的 trait。因此,我们需要为 Standard 实现 Distribution trait,以允许随机生成。

以下实例代码引用自开源书籍项目《Cookin’ with Rust》,笔者在其基础上稍作修改。

use rand::Rng;
use rand::distributions::{Distribution, Standard};

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Distribution<Point> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
        let (rand_x, rand_y) = rng.gen();
        Point {
            x: rand_x,
            y: rand_y,
        }
    }
}

fn main() {
    let mut rng = rand::thread_rng();
    let rand_tuple = rng.gen::<(i32, bool, f64)>();
    let rand_point: Point = rng.gen();

    println!("  随机值元组:   {:?}", rand_tuple);
    println!("  随机值结构体: {:?}", rand_point);
}

这段代码行数虽多,但逻辑不复杂。

代码第 1,2 行,使用 use 将相关模块引入作用域。

代码第 5-8 行,自定义一个结构体类型 Point

最主要的是代码第 10-18 行,为 Standard 实现 Distribution trait,其中 Distribution trait 的类型为结构体 Point。这样可以在 sample 方法中,直接将产生的随机值封装到结构体 Point

构建并运行后,结果大抵如下所示。

  随机值元组:   (413049996, true, 0.19354408882040275)
  随机值结构体: Point { x: 1087377718, y: -375119726 }

注:你的运行结果值和笔者运行结果不一定相同。

讨论:

对于用户自定义类型,我们需要使用通用的随机值分布结构体 Standard,然后为结构体 Standard 实现 Distribution trait。结构体 Standard 为 rand crate 中的众多基本类型实现,也可适用于用户自定义类型。而 Distribution trait 则用于创建概率分布类型,可以用来创建泛型 T 的随机实例的类型。提供 sample_iter 方法,该方法生成一个迭代器,从分布中采样。