如何创建包含引用的结构的Quickcheck任意?

时间:2016-04-03 20:32:56

标签: rust quickcheck

Rust quickcheck documentation注意到任何实现Arbitrary

的类型
  

它们也必须是可发送的和静态的,因为每个测试都使用thread :: Builder :: spawn在自己的线程中运行,这需要Send +'静态边界。

如果我需要为包含引用的结构生成数据,我该怎么做呢?例如:

#![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`.

这是一个有点人为的例子,但它与原始问题的精神相同。除了测试之外,生命周期注释可以解决。

1 个答案:

答案 0 :(得分:4)

由于两个原因,你无法做到这一点。首先,Arbitrary绑定'static,这意味着实现Arbitrary的类型可能没有引用,除非它们的生命周期为'static。这可以确保实例不引用它们不“拥有”的对象。

其次,为了返回C<'a> 'a'static以外的任何内容,大部分时间您还需要一个包含具有相同生命周期的引用的参数参数(并不总是必要的,例如,当使用life参数的字段可以在以后初始化时,但这不适用于此)。因此,你需要一个像这样定义的函数:

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()返回Self。这意味着该函数必须返回实现Arbitrary的类型。但是,C<'a>取决于函数上定义的生命周期参数; C<'a>不能与impl目标相同,因为该类型不能使用该生命周期参数。
  • Rng::gen()只是调用Rand::rand(),它也返回Self,因此遇到与Arbitrary::arbitrary()相同的问题。另外,Rand未实现&str(甚至String)。

你能做什么呢?您应该存储&str,而不是在结构中存储String。这使您的结构'static成为可能,您可以使用Arbitrary的实现来生成String来生成测试值。

但是,如果您不想在实际应用程序代码中使用String,该怎么办?您可以通过接受&strString来使结构具有通用性。标准库中有两个特性可帮助您执行此操作:AsRefBorrow。以下是使用Borrow的示例:

use std::borrow::Borrow;

#[derive(Debug, Clone)]
pub struct C<S: Borrow<str>> {
    s: S,
    b: bool
}

现在,您可以使用C<&str>C<String>,具体取决于所需内容。显然,您无法为Arbitrary实施C<&str>,但您可以为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 } } } 的所有类型实现它?

Function