如何在Rust中安全生成泛型类型的Arbitrary实例?
你遇到的生命周期错误根源在于:Trait<'a, T: Arbitrary<'a>> 将 T 的 Arbitrary 生命周期参数与 trait 的 'a 绑定,而栈上的 randomness 生命周期仅局限于 foo 函数内部,无法满足 'a 可能要求的更长生命周期(编译器无法确定 'a 不会超过 foo 的栈帧)。
当 T: Arbitrary<'a> 时,T 可能持有对输入 &'a [u8] 的引用,栈上的 randomness 在函数结束后会被销毁,导致潜在的悬垂引用,因此编译器报错。
方案1:使用高阶生命周期解除绑定
修改 trait 和实现,让 T 对任意生命周期都能实现 Arbitrary,这样就不需要依赖特定的 'a:
use arbitrary::Arbitrary; use arbitrary::Unstructured; use rand::thread_rng; use rand::Rng; extern crate arbitrary; struct A; // 使用高阶生命周期,T 对任意生命周期 'b 都实现 Arbitrary<'b> trait Trait<T> where T: for<'b> Arbitrary<'b>, { fn foo(&self) -> Vec<u128>; } fn random_object<T>(randomness: &[u8]) -> T where T: for<'b> Arbitrary<'b>, { let unstructured = Unstructured::new(randomness); T::arbitrary_take_rest(unstructured).unwrap() } impl<T> Trait<T> for A where T: for<'b> Arbitrary<'b>, { fn foo(&self) -> Vec<u128> { let mut rng = thread_rng(); let mut randomness = [0u8; 1000]; rng.fill_bytes(&mut randomness); let t: T = random_object(&randomness); // 这里可以使用 t vec![42] } }
这种方式适用于不持有输入数据引用的 T(大部分自定义类型都属于这种情况,比如用 #[derive(Arbitrary)] 生成的无引用类型)。
方案2:将随机数据放在堆上并延长生命周期(如果T需要持有引用)
如果 T 确实需要持有输入数据的引用,你可以将随机数据存储在堆上(比如 Vec<u8>),并确保其生命周期覆盖 T 的使用范围。例如,在需要使用 T 的作用域内保留 Vec:
use arbitrary::Arbitrary; use arbitrary::Unstructured; use rand::thread_rng; use rand::Rng; extern crate arbitrary; struct A; trait Trait<'a, T: Arbitrary<'a>> { fn foo(&self) -> (Vec<u128>, T); // 返回 T,让其生命周期与堆上数据绑定 } fn random_object<'a, T: Arbitrary<'a>>(randomness: &'a [u8]) -> T { let unstructured = Unstructured::new(randomness); T::arbitrary_take_rest(unstructured).unwrap() } impl<'a, T: Arbitrary<'a>> Trait<'a, T> for A { fn foo(&self) -> (Vec<u128>, T) { let mut rng = thread_rng(); // 使用 Vec 存储随机数据,生命周期由外部作用域管理 let mut randomness = vec![0u8; 1000]; rng.fill_bytes(&mut randomness); let t: T = random_object(&randomness); (vec![42], t) } }
这里 randomness 是堆上的 Vec,返回的 T 持有对它的引用,而 Vec 和 T 一起返回,确保生命周期一致。
方案3:直接使用 arbitrary 的内置随机生成能力
arbitrary 本身可以配合 rand 直接生成实例,无需手动创建随机字节数组。可以使用 arbitrary::Unstructured 从 rand 的 Rng 生成数据:
use arbitrary::Arbitrary; use arbitrary::Unstructured; use rand::thread_rng; use rand::RngCore; extern crate arbitrary; struct A; trait Trait<T> where T: for<'b> Arbitrary<'b>, { fn foo(&self) -> Vec<u128>; } fn random_object<T>() -> T where T: for<'b> Arbitrary<'b>, { let mut rng = thread_rng(); // 生成足够的随机字节(可根据需要调整大小) let mut buf = vec![0u8; 1000]; rng.fill_bytes(&mut buf); let unstructured = Unstructured::new(&buf); T::arbitrary_take_rest(unstructured).unwrap() } impl<T> Trait<T> for A where T: for<'b> Arbitrary<'b>, { fn foo(&self) -> Vec<u128> { let t: T = random_object(); // 使用 t vec![42] } }
这种方式更简洁,无需手动传递随机字节数组,同时避免生命周期问题。
你用 static mut 存储随机数据,使其拥有 'static 生命周期,满足了 T: Arbitrary<'static> 的约束,但这种方式存在线程安全风险(多个线程同时修改 static mut 会导致未定义行为),因此不推荐在多线程环境中使用。
内容的提问来源于stack exchange,提问作者Thorkil Værge

