How to create a Quickcheck Arbitrary of a struct containing a reference?

troutwine

The Rust quickcheck documentation notes that for any type implementing 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.

If I need to generate data for a struct that contains a reference how do I go about doing that? For instance:

#![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
    }
}

fails with

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`.

This is a somewhat contrived example but it's in the same spirit as the originating problem. The lifetime annotations work out except but under test.

Francis Gagné

You can't do this for two reasons. First, Arbitrary has a 'static bound, which means that the types that implement Arbitrary may not have references, unless their lifetime is 'static. This ensures that instances don't refer to objects that they don't "own".

Second, in order to return a C<'a> where 'a is anything other than 'static, most of the time you'll also need a parameter that contains a reference with the same lifetime parameter (it's not always necessary, e.g. when the field using the lifetime parameter can be initialized later, but that doesn't apply here). Therefore, you'd need a function defined a bit like this:

fn arbitrary<'a, G: Gen>(g: &'a mut G) -> C<'a> {
    let s = g.gen::<&str>();
    C { s: s, b: (s.len() > 0) }
}

(Note that 'a is defined on the function, not on the impl.)

There are two big problems with this:

  • Arbitrary::arbitrary() returns Self. This means that the function must return the type on which Arbitrary is implemented. Here, however, C<'a> depends on a lifetime parameter defined on the function; C<'a> cannot possibly be the same as the impl target, since that type cannot use that lifetime parameter.
  • Rng::gen() simply calls Rand::rand(), which also returns Self, and thus suffers from the same problem as Arbitrary::arbitrary(). Also, Rand is not implemented for &str (or even for String).

What can you do instead? Instead of storing a &str in your struct, you should store a String instead. This makes your struct 'static, and you can use the implementation of Arbitrary for String to generate test values.

But what if you don't want to use String in your actual application code? You can make your struct generic by accepting either &str or String. There are two traits in the standard library that help you do this: AsRef and Borrow. Here's an example using Borrow:

use std::borrow::Borrow;

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

Now, you can use either C<&str> or C<String>, depending on what's needed. Obviously, you can't implement Arbitrary for C<&str>, but you can implement it for C<String>. Actually, why not implement it for all types that implement 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 }
    }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to create a blob containing an arbitrary octet in Javascript

How to organize definition with QuickCheck Arbitrary class in Haskell

how to create a struct/model in golang with dynamic/arbitrary fields

flutter how to create an dart:ffi struct reference

How do I create a struct containing a nalgebra matrix with higher dimensions?

How to create a unmanaged struct in powershell containing plain arrays?

create struct containing fields of another struct

How to create hashmap containing reference to another hashmap's element?

Quickcheck: produce arbitrary elements of an arbitrary set

Is it possible to generate arbitrary functions in QuickCheck

How to grep for arbitrary string containing special characters

impossible for struct to meaningfully store reference with longer lifetime than the containing struct?

Is it possible to instantiate a new struct, from a variable, containing a reference to that struct?

Rust: How to create a mutable reference to a struct's member from another member during Struct::new()?

How to create a string of arbitrary length

How to create an arbitrary link in Rails?

How do I write a QuickCheck property that arbitrarily modifies its arbitrary input?

How to access the struct attribute inside a struct by reference

How do you create quickCheck properties with a Property output in Haskell?

QuickCheck Generator - Arbitrary element of custom type

Monadic QuickCheck test not working for arbitrary Repa array

Arbitrary instance for generating unbiased graphs for quickcheck

How to reference the struct in the same package

How to clone struct with a mutable reference

Is it ever possible to create a reference to element of array of struct?

how to create struct in julia?

How to create a generic struct?

How to find text in MySQL containing arbitrary number of double and single quotations

ArrayList containing arbitrary objects