-
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 fill and try_fill methods to Rng #247
Conversation
I have been working on something similar, but with a different approach. pitdicker@bd44cd2 is the latest try. Every type there has a default implementation that uses the same method as |
Interesting; you push more of the implementation down into the trait. Your version also supports any type supporting the I'm not sure which I prefer; I'm also not sure how useful the error forwarding would be in practice (with many generators being infallible, and for the rest a single "try generating a random word" check after initialisation would suffice to catch most errors). |
Good summary! For some things like distributions returning To add a wrapper like Sorry for bringing up a different issue though... |
No, I don't think much of that. If the wrapper is implementing the same |
Are you ready to make a choice between the two options? I like mine more 😄. But my variant does nothing that can not be achieved in some other way just as easy and fast. While yours can also pass on errors, so that seems like the better choice. |
I added a second commit inspired by @pitdicker's version. There is however a significant difference: @pitdicker's code also supports any type supported by the default distribution, e.g. arrays, slices and floating-point numbers, so for example it could fill an array with FP numbers in the range So, what should this do? let mut array = [0f32; 100];
rng.fill(&mut array); // or &mut array[..]
Pretty obviously, option (2) has very little use (and would not be safe with all types). Option (1) is well-defined but appears counter-intuitive (or maybe that's just me) — it requires accessing elements individually and using a distribution, instead of just a block-copy. I prefer (3) but others may prefer (1)? |
src/lib.rs
Outdated
/// Trait for casting types to byte slices | ||
pub trait AsByteSlice { | ||
/// Return a mutable reference to self as a byte slice | ||
fn as_byte_slice<'a>(&'a mut self) -> &'a mut [u8]; |
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 don't know for sure (still a novice and no computer right now), but is it possible to end up with two mutable references to the same memory after this function?
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.
The reference passed in will be locked until after the result is destroyed thanks to Rust's lifetime analysis. It's actually hard to get this wrong in Rust.
src/lib.rs
Outdated
/// Return a mutable reference to self as a byte slice | ||
fn as_byte_slice<'a>(&'a mut self) -> &'a mut [u8]; | ||
/// Perform byte-swapping on BE platforms | ||
fn to_le(&mut self); |
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.
This does not really need to be part of the trait. Wouldn't it be easier to just write the conversion loop in fill
and try_fill
?
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.
Okay, then we can also standardise the naming.
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 will require to use more complex trait bounds, not only you'll need trait for iteration, but also for to_le
method as well.
Looks good to me! |
@burdges @newpavlov you commented on this topic before; any thoughts on this PR or @pitdicker's version? I'm not sure whether to add this, some other variant of |
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 don't mind this addition, but I think we better to list primitive types (i.e. u16
, u32
, etc.) in the methods documentation and add to AsByteSliceMut
note that its main usage is for fill
and try_fill
methods.
src/lib.rs
Outdated
let slice = dest.as_byte_slice_mut(); | ||
self.fill_bytes(slice); | ||
for mut x in slice { | ||
x.to_le(); |
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.
If I am reading this correctly this line does not do anything, because to_le
for u8
should be no-op. We'll need to add to_le
method to AsByteSliceMut
trait.
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.
You're right. Actually, that's what I had originally.
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.
Good catch, but shouldn't it just loop over dest
?
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 don't see a trait supporting to_le
though so it has to be done per implementation.
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.
Then you'll need to add another bound to allow you iterate mutably over dest
. I think it's easier to add a method to AsByteSliceMut
.
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 wonder if eventually we'll get a suitable trait; for now it doesn't look like it (also nothing in the num
crate I can see).
src/lib.rs
Outdated
fn to_le(&mut self) { | ||
for mut x in self { | ||
x.to_le(); | ||
} |
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 think this will not work. You need to write:
for x in self {
*x = x.to_le();
}
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 spotted. There's a unit test already but I we need to run CI on a BE platform...
80696ec
to
746a7aa
Compare
@newpavlov I added some documentation. Because this works by filling An advantage of doing it that way would be that we could still add a more generic |
Yes, I think this reasonable. Also, |
Regarding floats, see this discussion. I'd prefer to leave them out of this discussion since there are far too many possible trade-offs. |
I agree, which is why I think it is a good idea to only have |
My previous idea of replacing We could still use those names for the new functions and rename or shadow the old functions, but I don't think either is a good idea, so |
Rebased on top of master (with new |
Looks good to me! I would consider explicitly mentioning that the bytes are uniformly distributed. |
I have a commit adding support for arrays (up to length 32) using recursive expansion like
to:
but with some re-thinking, I got this down to:
Once support for generic constants lands we should be able to support arrays properly (without all the macro recursion nonsense). |
Usage of recursive macros appears to have some compile time hit, but with a single macro and generic impl it's not too much (directly recursing for each type is far worse).
Rebase after merge of #256. |
Add fill and try_fill methods to Rng
This was suggested a couple of times. It's perhaps more useful with #244 since then
fill_bytes
andtry_fill_bytes
are not available inSampleRng
.I'm not entirely happy with the code organisation; this adds a generic-sounding trait name, but perhaps it should be renamed
AsByteSliceMut
and not havefn to_le
to meet what would be expected from the name. From our point of view those are needless complications however.This also adds more unsafe code but I don't think that's avoidable.