Rust quickcheck 文档指出,对于任何类型的实现Arbitrary
它们还必须是可发送的和静态的,因为每个测试都使用 thread::Builder::spawn 在自己的线程中运行,这需要 Send + 'static 边界。
如果我需要为包含引用的结构生成数据,我该怎么做?例如:
#![cfg_attr(test, feature(plugin))]
#![cfg_attr(test, plugin(quickcheck_macros))]
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
use quickcheck::{Arbitrary,Gen};
#[allow(dead_code)]
#[derive(Debug,Clone)]
pub struct C<'a> {
s: &'a str,
b: bool
}
#[cfg(test)]
impl<'a> Arbitrary for C<'a> {
fn arbitrary<G: Gen>(g: &mut G) -> C<'a> {
let s = g.gen::<&str>();
C{s: s, b: (s.len() > 0)}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[quickcheck]
fn len_checks_out(c: C) -> bool {
(c.s.len() > 0) == c.b
}
}
Run Code Online (Sandbox Code Playgroud)
失败
cargo test
Compiling qcq v0.1.0 (file:///Users/blt/projects/us/troutwine/qcquestion)
src/lib.rs:18:10: 18:19 error: the type `C<'a>` does not fulfill the required lifetime [E0477]
src/lib.rs:18 impl<'a> Arbitrary for C<'a> {
^~~~~~~~~
note: type must outlive the static lifetime
error: aborting due to previous error
Build failed, waiting for other jobs to finish...
error: Could not compile `qcq`.
Run Code Online (Sandbox Code Playgroud)
这是一个有点人为的例子,但它与原始问题的精神相同。生命周期注释工作,但正在测试中。
你不能这样做有两个原因。首先,Arbitrary具有'static边界,这意味着实现的类型Arbitrary可能没有引用,除非它们的生命周期是'static. 这确保实例不会引用它们不“拥有”的对象。
其次,为了返回C<'a>where'a不是'static,大多数时候你还需要一个包含具有相同生命周期参数的引用的参数(这并不总是必要的,例如当使用生命周期参数的字段可以被初始化时稍后,但这不适用于此处)。因此,您需要一个像这样定义的函数:
fn arbitrary<'a, G: Gen>(g: &'a mut G) -> C<'a> {
let s = g.gen::<&str>();
C { s: s, b: (s.len() > 0) }
}
Run Code Online (Sandbox Code Playgroud)
(注意,'a是在函数上定义的,而不是在impl.)
这有两个大问题:
Arbitrary::arbitrary()返回Self。这意味着该函数必须返回实现的类型Arbitrary。然而,这里C<'a>依赖于定义在函数上的生命周期参数;C<'a>不可能与impl目标相同,因为该类型不能使用该生命周期参数。Rng::gen()简单地调用Rand::rand(),它也返回Self,因此遇到与Arbitrary::arbitrary(). 此外,Rand没有为&str(甚至为String)实现。你能做什么呢?而不是&str在您的结构中存储 a ,您应该存储 a String。这使您的 struct 'static,您可以使用Arbitraryfor的实现String来生成测试值。
但是,如果您不想String在实际应用程序代码中使用怎么办?您可以通过接受&str或使您的结构通用String。标准库中有两个特性可以帮助您做到这一点:AsRef和Borrow. 这是一个使用示例Borrow:
use std::borrow::Borrow;
#[derive(Debug, Clone)]
pub struct C<S: Borrow<str>> {
s: S,
b: bool
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以根据需要使用C<&str>或C<String>。显然,你不能实现Arbitraryfor C<&str>,但你可以实现它 for C<String>。实际上,为什么不为所有实现它的类型实现它Arbitrary?
impl<S: Borrow<str> + Arbitrary> Arbitrary for C<S> {
fn arbitrary<G: Gen>(g: &mut G) -> C<S> {
let s: S = Arbitrary::arbitrary(g);
let b = s.borrow().len() > 0;
C { s: s, b: b }
}
}
Run Code Online (Sandbox Code Playgroud)