Rust quickcheck documentation指出,对于实现Arbitrary的任何类型



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

#![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绑定(bind)了'static,这意味着实现Arbitrary的类型可能没有引用,除非它们的生存期是'static。这样可以确保实例不会引用不属于自己的对象。

其次,为了返回C<'a>而不是'a'static,大多数情况下,您还需要一个包含具有相同生命周期参数的引用的参数(并非总是必需的,例如,当字段使用lifelife参数时可以稍后再初始化,但这不适用于此处)。因此,您需要一个定义如下的函数:

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来使结构通用。标准库中有两个特征可以帮助您做到这一点: AsRef Borrow 。这是使用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 }
        }
    }
    

    10-08 02:58