New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Add auto-derivators for structs and enums #98
Comments
I've often thought of having something like this, but in practice, almost every Popping up a level, I'm a little down on plugins since their stabilization future is probably still a far way off. As such, I personally probably won't work on something like this any time soon. |
Bumping this since proc-macros are getting stabilised in a week or two, and a derive-macro is very easy to implement. This would be potentially very useful for generating |
If automatic derivation is problematic, then maybe a generator that wrote a proposed |
Macros 1.1 now allows custom I think the key puzzle is how to handle additional constraints, such as the ones I described in #163. Maybe we should try brainstorming what these constraints look like in typical cases. Here's my example: #[derive(Arbitrary, Clone)]
pub struct Rect {
left: usize,
top: usize,
width: usize,
height: usize,
} Constraints: We can actually add attributes like |
Big research dump ahead! This discussion has links to some prior art and research. There's also an interesting discussion of the tradeoffs involved in default instance distribution, although this mostly seems to affect the size distribution of recursive data structures such as lists (that are considerably rarer in Rust than Haskell). The two papers they mention can be found here and here. While these papers look interesting, I'm not sure if they're directly relevant. So there are some reasons why Another question is the relationship between So there are a bunch of ways to tackle this, but it's hard to proceed without more examples. So let's dig a bit:
Here's a code snippet from #[cfg(feature="arbitrary")]
impl<N, S> Arbitrary for PerspectiveBase<N, S>
where N: Real + Arbitrary,
S: OwnedStorage<N, U4, U4> + Send,
S::Alloc: OwnedAllocator<N, U4, U4, S> {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let znear = Arbitrary::arbitrary(g);
let zfar = helper::reject(g, |&x: &N| !(x - znear).is_zero());
let aspect = helper::reject(g, |&x: &N| !x.is_zero());
Self::new(aspect, Arbitrary::arbitrary(g), znear, zfar)
}
} So custom
I think it would be both desirable and possible to automate (1) somehow, but that's a separate discussion. I'm interested in (2) and (3). I could imagine a syntax like: #[derive(Arbitrary, Clone)]
pub struct Rect {
left: usize,
top: usize,
#[arbitrary(constraint = "left.checked_add(width).is_some()")]
width: usize,
#[arbitrary(constraint = "top.checked_add(height).is_some()")]
height: usize,
} A naive implementation might write: let left = g.gen();
let width = g.gen_constrained(|width| left.checked_add(width).is_some());
let top = g.gen();
let height = g.gen_constrained(|height| top.checked_add(height).is_some());
Rect { left: left, width: width, top: top, height: height } ...but this would tend to fail if for _ in 0..MAX_TRIES {
let left = g.gen();
let width = g.gen_constrained(|width| left.checked_add(width).is_some());
let top = g.gen();
let height = g.gen_constrained(|height| top.checked_add(height).is_some());
return Rect { left: left, width: width, top: top, height: height };
}
panic!("could not generate instance of Rect; check your constraints"); Anyway, this is just a very rough sketch. But it would work for the examples of cases (2) and (3) that I found, and it wouldn't be especially hard to implement on stable Rust. What are some possible ways we could tweak this to make it better? |
Some of this code could theoretically be generated using Macros 1.1; see BurntSushi/quickcheck#98 for further discussion.
I'm gonna take a swing at this without constraints; I'll post here if I get it working. |
https://github.com/remexre/quickcheck_derive seems to be working? If people want to try it (and rate my first proc-macro), feel free. I'll probably move it into this repo's workspace and PR once I've implemented constraints and shrinking. edit: Use https://github.com/remexre/quickcheck/tree/derive instead, I'm planning to PR soon |
What should the behavior be for trying to derive Arbitrary on an empty enum? It doesn't really make sense to do, so maybe it should be disallowed? But I might want to have e.g. an Arbitrary Right now, it results in a run-time panic, which is probably the least optimal solution. |
Also, I've done a bit of work on constraints, and it's not been great. I'd actually like to see I'm going to push what I've got (as https://github.com/remexre/quickcheck_derive/tree/constraints-dead-end), then work on a the above approach. If anyone has better ideas, certainly share them. |
Maybe to handle empty enums properly,
should be changed to return |
We're not changing the API of the @remexre Not sure what to do about |
I don't see trying to test an enum containing a Void (possibly because of generic parameters) as a bug in the user's code per se, but I agree that it probably doesn't merit an API change. The problem is that the code gets a lot more complex when I'm trying to figure out whether an enum branch is possible or not as I'm doing codegen. I'm leaning towards it being a compile-time error for now; the panic is from rand, not quickcheck, so it's a bit bad UX. The problem with Splitting |
@remexre RE shrinkable: That's interesting. I grant that the use case is valid. I wonder how often that's required though, and when it is, I imagine writing out the But, I am open to splitting the trait, but I'm kind of leaning towards "there's not enough justification" presently... |
Okay, for now I'll write it so I could add |
This can probably be closed when #175 gets merged. |
I want quickcheck to have a compiler plugin that automatically generates Arbitrary impl for structs and enums composed of Arbitrary types, like with serialisation.
That should shrink
to
The text was updated successfully, but these errors were encountered: