Use param type to help Rust inference
Opened this issue · 3 comments
Currently, if I try to use types like this:
#[proptest]
fn test_ra(#[strategy(0..=24)] h: u8, #[strategy(0..60)] m: u8, #[strategy(0..60)] s: u8) {
I get an error like:
error[E0271]: type mismatch resolving `<RangeInclusive<i32> as proptest::strategy::Strategy>::Value == u8`
--> src\coords\astro.rs:279:27
|
279 | fn test_ra(#[strategy(0..=24)] h: u8, #[strategy(0..60)] m: u8, #[strategy(0..60)] s: u8) {
| ^ expected `u8`, found `i32`
|
note: required by a bound in `_strategy_of_h`
--> src\coords\astro.rs:279:39
|
278 | #[proptest]
| ----------- required by a bound in this
279 | fn test_ra(#[strategy(0..=24)] h: u8, #[strategy(0..60)] m: u8, #[strategy(0..60)] s: u8) {
| ^^ required by this bound in `_strategy_of_h`
So, just like when using strategies directly in proptest!
, I have to add suffixes to clarify which type I meant and then it works:
#[proptest]
fn test_ra(#[strategy(0..=24_u8)] h: u8, #[strategy(0..60_u8)] m: u8, #[strategy(0..60_u8)] s: u8) {
However, test_strategy
has more info that regular proptest!
macro does - namely, it has access to explicit parameter types. Could it perhaps pass those through to corresponding generics to make inference work correctly without explicit suffixes?
test_strategy
generates the following code to get the value of the type that implements Strategy<Value=u8>
.
This code causes that error because type inference does not work.
use proptest::strategy::Strategy;
use std::fmt::Debug;
fn _strategy_of_h<T: Debug, S: Strategy<Value = T>>(s: S) -> impl Strategy<Value = T> {
s
}
let _s = _strategy_of_h::<u8, _>(0..=24);
If I could find some code where type inference works, I could fix it, but I don't know how to write such code?
If we cannot find a way to get type inference to work correctly, adding a suffix by the macro may be an option.
However, this will work for simple cases such as 1..=24
, but will not work for complex cases such as (1..=24).prop_map(|x| x * 2)
because it cannot determine if adding a suffix is appropriate.
I see. It does look like test-strategy already does what it can. Too bad that Strategy<Value = u8>
is not enough for Rust to guess which Range<T>
implementation to pick.
I wondered why it could be inferred for Iterator
but not for Strategy
. It seems to be due to the difference in whether the trait was implemented individually or generically.
The inference succeeded for Strategy1
in the following code but failed for Strategy2
.
use std::ops::Range;
trait Strategy1 {
type Value;
}
impl<T> Strategy1 for Range<T> {
type Value = T;
}
trait Strategy2 {
type Value;
}
impl Strategy2 for Range<u8> {
type Value = u8;
}
impl Strategy2 for Range<i32> {
type Value = i32;
}
fn s1(s: impl Strategy1<Value = u8>) {}
fn s2(s: impl Strategy2<Value = u8>) {}
s1(0..10);
s2(0..10); // error[E0271]: type mismatch resolving `<std::ops::Range<i32> as Strategy2>::Value == u8`