如何创建包含引用的结构的 Quickcheck Arbitrary?
How to create a Quickcheck Arbitrary of a struct containing a reference?
Rust quickcheck documentation 指出对于任何实现 Arbitrary
的类型
They must also be sendable and static since every test is run in its own thread using thread::Builder::spawn, which requires the Send + 'static bounds.
如果我需要为包含引用的结构生成数据,我该怎么做?例如:
#![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
}
}
失败
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`.
这是一个有点人为的例子,但它与原始问题的精神是一样的。生命周期注解可以解决,但正在测试中。
您不能这样做有两个原因。首先,Arbitrary
有一个 'static
绑定,这意味着实现 Arbitrary
的类型可能没有引用,除非它们的生命周期是 'static
。这确保实例不会引用它们不“拥有”的对象。
其次,为了 return a C<'a>
where 'a
is anything other than 'static
, most time you'll need a parameter that contains具有相同生命周期参数的引用(并不总是必要的,例如,当使用生命周期参数的字段可以稍后初始化时,但此处不适用)。因此,你需要一个像这样定义的函数:
fn arbitrary<'a, G: Gen>(g: &'a mut G) -> C<'a> {
let s = g.gen::<&str>();
C { s: s, b: (s.len() > 0) }
}
(注意 'a
是在函数上定义的,而不是在 impl
上定义的。)
这有两个大问题:
Arbitrary::arbitrary()
returns Self
。这意味着该函数必须 return 实现 Arbitrary
的类型。但是,这里 C<'a>
取决于在函数 上定义的生命周期参数 ; C<'a>
不可能与 impl
目标相同,因为该类型不能使用该生命周期参数。
Rng::gen()
简单地调用 Rand::rand()
,它也 returns Self
,因此遇到与 Arbitrary::arbitrary()
相同的问题。此外,Rand
未针对 &str
(甚至 String
)实施。
那你能做什么?您不应在结构中存储 &str
,而应存储 String
。这使得你的结构 'static
,你可以使用 Arbitrary
的实现来为 String
生成测试值。
但是如果您不想在实际的应用程序代码中使用 String
怎么办?您可以通过接受 &str
或 String
使您的结构通用。标准库中有两个特征可以帮助您做到这一点:AsRef
and Borrow
。这是一个使用 Borrow
:
的例子
use std::borrow::Borrow;
#[derive(Debug, Clone)]
pub struct C<S: Borrow<str>> {
s: S,
b: bool
}
现在,您可以根据需要使用 C<&str>
或 C<String>
。显然,你不能为C<&str>
实现Arbitrary
,但是你可以为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 }
}
}
Rust quickcheck documentation 指出对于任何实现 Arbitrary
They must also be sendable and static since every test is run in its own thread using thread::Builder::spawn, which requires the Send + 'static bounds.
如果我需要为包含引用的结构生成数据,我该怎么做?例如:
#![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
}
}
失败
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`.
这是一个有点人为的例子,但它与原始问题的精神是一样的。生命周期注解可以解决,但正在测试中。
您不能这样做有两个原因。首先,Arbitrary
有一个 'static
绑定,这意味着实现 Arbitrary
的类型可能没有引用,除非它们的生命周期是 'static
。这确保实例不会引用它们不“拥有”的对象。
其次,为了 return a C<'a>
where 'a
is anything other than 'static
, most time you'll need a parameter that contains具有相同生命周期参数的引用(并不总是必要的,例如,当使用生命周期参数的字段可以稍后初始化时,但此处不适用)。因此,你需要一个像这样定义的函数:
fn arbitrary<'a, G: Gen>(g: &'a mut G) -> C<'a> {
let s = g.gen::<&str>();
C { s: s, b: (s.len() > 0) }
}
(注意 'a
是在函数上定义的,而不是在 impl
上定义的。)
这有两个大问题:
Arbitrary::arbitrary()
returnsSelf
。这意味着该函数必须 return 实现Arbitrary
的类型。但是,这里C<'a>
取决于在函数 上定义的生命周期参数 ;C<'a>
不可能与impl
目标相同,因为该类型不能使用该生命周期参数。Rng::gen()
简单地调用Rand::rand()
,它也 returnsSelf
,因此遇到与Arbitrary::arbitrary()
相同的问题。此外,Rand
未针对&str
(甚至String
)实施。
那你能做什么?您不应在结构中存储 &str
,而应存储 String
。这使得你的结构 'static
,你可以使用 Arbitrary
的实现来为 String
生成测试值。
但是如果您不想在实际的应用程序代码中使用 String
怎么办?您可以通过接受 &str
或 String
使您的结构通用。标准库中有两个特征可以帮助您做到这一点:AsRef
and Borrow
。这是一个使用 Borrow
:
use std::borrow::Borrow;
#[derive(Debug, Clone)]
pub struct C<S: Borrow<str>> {
s: S,
b: bool
}
现在,您可以根据需要使用 C<&str>
或 C<String>
。显然,你不能为C<&str>
实现Arbitrary
,但是你可以为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 }
}
}