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.
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.
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.
| /// 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.
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.
Okay, then we can also standardise the naming.
There was a problem hiding this comment.
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 |
newpavlov
left a comment
There was a problem hiding this comment.
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.
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.
You're right. Actually, that's what I had originally.
There was a problem hiding this comment.
Good catch, but shouldn't it just loop over dest?
There was a problem hiding this comment.
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.
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.
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).
| fn to_le(&mut self) { | ||
| for mut x in self { | ||
| x.to_le(); | ||
| } |
There was a problem hiding this comment.
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.
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_bytesandtry_fill_bytesare 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
AsByteSliceMutand not havefn to_leto 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.