1

I'm using quickcheck and would like to implement quickcheck::Arbitrary for a struct. This trait has to exist in the same file/crate that the struct is defined, but I don't want it in the release binary.

pub struct c_struct {
    pub i64_: i64,
    pub u64_: u64,
    pub u32_: u32,
}

// #[cfg(test)] does not work
impl quickcheck::Arbitrary for c_struct {
    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> c_struct {
        c_struct {
            i64_: i64::arbitrary(g),
            u64_: u64::arbitrary(g),
            u32_: u32::arbitrary(g),
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
davepmiller
  • 2,620
  • 3
  • 33
  • 61

2 Answers2

4

You can use the conditional compilation attribute #[cfg()] here:

pub struct c_struct {
    pub i64_: i64,
    pub u64_: u64,
    pub u32_: u32,
}

#[cfg(test)]
impl quickcheck::Arbitrary for c_struct {
    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> c_struct {
        c_struct {
            i64_: i64::arbitrary(g),
            u64_: u64::arbitrary(g),
            u32_: u32::arbitrary(g),
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Wesley Wiser
  • 9,491
  • 4
  • 50
  • 69
  • .quickcheck(prop_use_c_struct as fn(c_struct) -> bool); | ^^^^^^^^^^ the trait `quickcheck::Arbitrary` is not implemented for `rusty::rusty_c_struct::c_struct` – davepmiller Mar 06 '17 at 21:18
  • 1
    @laser_wizard that's very strange. The quickcheck documentation even mentions the `#[cfg(test)]` approach https://github.com/BurntSushi/quickcheck#simple-example – Wesley Wiser Mar 06 '17 at 21:33
  • @laser_wizard how are you running quickcheck? `cargo test` or something else? – Wesley Wiser Mar 06 '17 at 21:35
  • `cargo test`, yes. I thought I had it by adding the macro to my prop function, but I had removed the macro from the `impl` definition. – davepmiller Mar 06 '17 at 21:39
  • Hmm... The `test` symbol should be defined then so it should be picking up the implementation. – Wesley Wiser Mar 06 '17 at 21:42
  • As [Francis Gagné points out](http://stackoverflow.com/questions/42635556/is-it-possible-to-implement-a-trait-for-a-struct-only-while-running-tests/42636226?noredirect=1#comment72405421_42636226), you could also put the `impl Arbitrary` into an existing `cfg(test)` block, which you are likely to have anyway for unit tests. – Shepmaster Mar 07 '17 at 02:21
1

A common solution to this is to use a newtype that is only defined in the tests:

struct c_struct {
    pub i64_: i64,
    pub u64_: u64,
    pub u32_: u32,
}

#[cfg(test)]
mod test {
    struct ArbitraryCStruct(c_struct);

    impl quickcheck::Arbitrary for ArbitraryCStruct {
        fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> ArbitraryCStruct {
            ArbitraryCStruct(c_struct {
                i64_: i64::arbitrary(g),
                u64_: u64::arbitrary(g),
                u32_: u32::arbitrary(g),
            })
        }
    }
}

You can then accept this in your quickcheck function. If you need to, you can extract the value using .0 or implement the From or Into trait as needed.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • If `c_struct` is defined in the same crate, it's not necessary to define a newtype. You can just write `impl quickcheck::Arbitrary for super::c_struct`. – Francis Gagné Mar 07 '17 at 02:11
  • 1
    @FrancisGagné a good point; I do normally ignore the fact that that the implementation doesn't have to be "right next" to the type or the trait, so long as it's the the same crate as either. However, that's basically what [the other answer says](http://stackoverflow.com/a/42635679/155423), so I won't edit this question to avoid stealing their answer. – Shepmaster Mar 07 '17 at 02:20