-
Notifications
You must be signed in to change notification settings - Fork 432
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
Add tonnes of impls for Rand. #148
Conversation
src/rand_impls.rs
Outdated
Range { start: end, end: start } | ||
} else { | ||
Range { start: start, end: end } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not exactly sure why you’d cut half of the range here. Range with end < start
does make sense in some contexts and the type in question has no real requirements wrt ordering of start and end points within it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a fair point. I'll remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nagisa Wouldn't this cause issues with Range<usize>
or similar types where (correct me if I'm wrong) start
is expected to be less than end
?
src/lib.rs
Outdated
//! *all* possible values of the type, just to a reasonable amount. For example, | ||
//! `rand::random::<f64>()` will never return `f64::NAN`, and will only return | ||
//! a value in the range [0, 1). `Option<u8>` will have a 1/2 chance of being, | ||
//! `None`, not 1/257. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is widely accepted/known that floating point random numbers are generated in range 0 to 1. As for None
, if you ignore the NonNull
optimisation, it having a chance of ½ is uniform from the perspective of bit pattern, which is (discriminant, value).
I've removed the note in |
Bumping this. |
|
||
impl Rand for Ipv4Addr { | ||
#[inline] | ||
fn rand<R: Rng>(rng: &mut R) -> Ipv4Addr { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rng.gen::<i8>()
would prevent overflow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ipv4Addr
accepts u8
s. This uses type inference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this exclude certain special addresses perhaps, e.g. those ending in 0 or 255?
May be easiest and fastest to implement via looping on rejection.
@clarcharr Why? The only example I've come across so far where you might want random values of arbitrary types is fuzzing, but as I pointed out elsewhere this is very unlikely to generate certain special values that should probably be tested, e.g. 0, and may not give a sensible distribution for some types (e.g. Personally I see no use for these "default generators". If there is some use, e.g. fuzzing, then a specific distribution could be used instead ( |
@dhardy the main reason why I was thinking these were useful was for |
@clarcharr thanks for your input. It's not for me to decide whether to close this PR. Some of these like |
Bump; is this PR desired or should I just close it for now? I'm willing to update it based upon feedback, but I'm not sure what should be changed/removed at the moment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, some stuff like Wrapping
seems like it should be added IMO. Some of the distributions seem weird and some of those ranges could be huge (so one can't just iterate over a random range). Stuff like Box
which allocates... well, I can't see a real reason why it shouldn't be there. Adding extra features to Config.toml
I'm not keen on.
But mostly I'll just say: patience! The rand
refactor stuff will take a while, and there's not so much point in finalising this bit yet, although there's also no real reason it can't go ahead I guess (with the caveat that it may have to change later).
impl Rand for Duration { | ||
#[inline] | ||
fn rand<R: Rng>(rng: &mut R) -> Duration { | ||
Duration::new(rng.gen(), rng.gen_range(0, 1_000_000_000)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest using an exponential distribution here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I consider Duration
to be very similar to any other integer type, and so uniform probability makes the most sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How often are you working with hundred million year durations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@clarcharr that's a null argument considering there are many things people might want to do with random integers (virtual dice, randomised network delays, random sorting, random selection from a list (e.g. a set of Unicode glyphs, Poisson distributions) and probably none of them involve uniform sampling from a fixed range not specific to this use-case.
I suggested exponential here because that, or sampling from a short arbitrary range (e.g. 0 to 1 minutes) are about the only things that make any sense whatsoever when you don't know the target use.
match rng.gen_range(0, 3) { | ||
0 => Ordering::Less, | ||
1 => Ordering::Equal, | ||
2 => Ordering::Greater, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1/3 chance of each ordering may be okay, but it's arbitrary and even weird. Not really sure; it's not worse than any other option but may still not be what's wanted. Still, we already have Option
..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair! I was considering this for the sake of testing, and IMHO each should be equally likely to be tested.
impl<T: Rand + PartialOrd> Rand for Range<T> { | ||
#[inline] | ||
fn rand<R: Rng>(rng: &mut R) -> Range<T> { | ||
Range { start: rng.gen(), end: rng.gen() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to ensure end ≥ start
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comments from @nagisa.
|
||
impl Rand for Ipv4Addr { | ||
#[inline] | ||
fn rand<R: Rng>(rng: &mut R) -> Ipv4Addr { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this exclude certain special addresses perhaps, e.g. those ending in 0 or 255?
May be easiest and fastest to implement via looping on rejection.
#[inline] | ||
fn rand<R: Rng>(rng: &mut R) -> Ipv6Addr { | ||
Ipv6Addr::new(rng.gen(), rng.gen(), rng.gen(), rng.gen(), | ||
rng.gen(), rng.gen(), rng.gen(), rng.gen()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, should any addresses be omitted here? No idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're all valid addresses, even though many are not routable. So, IMHO this is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the problem with general implementations like this: you have no idea how they will be used. Given that special addresses are very rare and therefore unlikely to come up by accident, it seems sensible to specify that they won't come up at all.
} | ||
|
||
// TODO: remove Copy | ||
impl<T: Copy + Rand> Rand for Cell<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt you'll ever be able to remove Copy. So remove the TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, doing so only requires a minimum rustc bump.
nightly = ["i128_support"] | ||
bound = [] # std::collections is unstable in 1.0.0 | ||
duration = [] # Duration is unstable in 1.0.0 | ||
ip_addr = [] # IpAddr is unstable in 1.0.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not so keen on adding a bunch of conditional features here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed! These should be added properly with a rustc version bump.
All those interested: should we remove |
Now that @clarcharr I know there have been many months while your PR has been sitting here. But now that we have a clear idea of the future of the |
I'm not sure how many of these we want, but I just looked through the list and implemented as many as I found appropriate. I'm more than happy to prune the PR of unwanted impls.