Skip to content

Derive with custom strategy and generic type #587

@avandecreme

Description

@avandecreme

Using proptest 1.7.0 and proptest-derive 0.6, the following code compile fine:

use std::collections::BTreeMap;

use proptest::prelude::*;
use proptest_derive::{self, Arbitrary};

#[derive(Debug, Arbitrary)]
struct Foo<A: Ord> {
    bar: i32,

    // #[proptest(strategy = "proptest::collection::btree_map(any::<A>(), any::<i16>(), 0..5)")]
    baz: BTreeMap<A, i16>,
}

However, if I uncomment the #[proptest(strategy = "proptest::collection::btree_map(any::<A>(), any::<i16>(), 0..5)")] line, I get a compiler error:

the trait bound A: proptest::arbitrary::Arbitrary is not satisfied

Using cargo expand here are the generated code:

  1. without the custom strategy:
const _: () = {
    use proptest as _proptest;
    impl<A: Ord + _proptest::arbitrary::Arbitrary> _proptest::arbitrary::Arbitrary
    for Foo<A> {
        type Parameters = (
            <i32 as _proptest::arbitrary::Arbitrary>::Parameters,
            <BTreeMap<A, i16> as _proptest::arbitrary::Arbitrary>::Parameters,
        );
        type Strategy = _proptest::strategy::Map<
            ((
                <i32 as _proptest::arbitrary::Arbitrary>::Strategy,
                <BTreeMap<A, i16> as _proptest::arbitrary::Arbitrary>::Strategy,
            )),
            fn((i32, BTreeMap<A, i16>)) -> Self,
        >;
        fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy {
            {
                let (param_0, param_1) = _top;
                _proptest::strategy::Strategy::prop_map(
                    (
                        _proptest::arbitrary::any_with::<i32>(param_0),
                        _proptest::arbitrary::any_with::<BTreeMap<A, i16>>(param_1),
                    ),
                    |(tmp_0, tmp_1)| Foo { bar: tmp_0, baz: tmp_1 },
                )
            }
        }
    }
};

We can see the Arbitrary constraint in the impl block.

  1. with the custom strategy:
const _: () = {
    use proptest as _proptest;
    impl<A: Ord + ::std::fmt::Debug> _proptest::arbitrary::Arbitrary for Foo<A> {
        type Parameters = <i32 as _proptest::arbitrary::Arbitrary>::Parameters;
        type Strategy = _proptest::strategy::Map<
            ((
                <i32 as _proptest::arbitrary::Arbitrary>::Strategy,
                _proptest::strategy::BoxedStrategy<BTreeMap<A, i16>>,
            )),
            fn((i32, BTreeMap<A, i16>)) -> Self,
        >;
        fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy {
            {
                let param_0 = _top;
                _proptest::strategy::Strategy::prop_map(
                    (
                        _proptest::arbitrary::any_with::<i32>(param_0),
                        _proptest::strategy::Strategy::boxed(
                            proptest::collection::btree_map(
                                any::<A>(),
                                any::<i16>(),
                                0..5,
                            ),
                        ),
                    ),
                    |(tmp_0, tmp_1)| Foo { bar: tmp_0, baz: tmp_1 },
                )
            }
        }
    }
};

The Arbitrary constraint has been replaced by a Debug constraint.

It tried swapping the Debug and Arbitrary in the derive annotation but it doesn't change anything.

Here is the implementation I was hoping for:

impl<A: Ord + Arbitrary> Arbitrary for Foo<A> {
    type Parameters = ();

    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
        let bar = any::<i32>();
        let baz = proptest::collection::btree_map(any::<A>(), any::<i16>(), 0..5);

        (bar, baz).prop_map(|(bar, baz)| Foo { bar, baz })
    }

    type Strategy = proptest::strategy::Map<
        (
            <i32 as proptest::arbitrary::Arbitrary>::Strategy,
            <BTreeMap<A, i16> as proptest::arbitrary::Arbitrary>::Strategy,
        ),
        fn((i32, BTreeMap<A, i16>)) -> Self,
    >;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions