BurntSushi / quickcheck

Automated property based testing for Rust (with shrinking).
The Unlicense
2.32k stars 144 forks source link

Implement Arbitrary for AsMut<[T: Arbitrary]> #291

Open cyphar opened 3 years ago

cyphar commented 3 years ago

Because of #265, it's no longer possible to do something like

#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
struct Foo {
    foo: ChaChaPolyNonce, // has a BorrowMut<[u8]> impl
}

#[cfg(test)]
impl quickcheck::Arbitrary for Foo {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        let mut foo = ChaChaPolyNonce::default();
        g.fill_bytes(&mut foo); // No more RngCore!
        Self { foo }
    }
}

But it seems as though it would be reasonable to simply have an Arbitrary implementation for &mut [T: Arbitrary] that does fill_bytes internally.

neithernut commented 3 years ago

Having BorrowMut<[T: Arbitrary]> + Default types (or maybe some proxy/wrapper) implement Arbitrary is certainly possible. But how would you implement Arbitrary for & mut [T: Arbitrary] based on fill_bytes in the general case?

cyphar commented 3 years ago

Sorry, I was trying to generalise the original issue I had (being unable to use fill_bytes with the new quickcheck release) into being able to handle arbitrary &mut[T: Arbitrary] without adjusting how you would actually solve it.

I also forgot that arbitrary doesn't take &mut self or anything so you'd need to implement it as a Gen method instead, so something more like:

impl Gen {
    /// Fill a mutable slice of any Arbitrary-compatible type with Arbitrary
    /// values.
    pub fn fill_slice<S, T>(&mut self, mut slice: S)
    where
        T: Arbitrary,
        S: AsMut<[T]>,
    {
        slice.as_mut().fill_with(|| T::arbitrary(self))
    }
}

I might just send a PR with this because the above works for me.