-
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
rand_core:0.5.0::Error
has no raison d'etre
#837
Comments
Indeed. Usable I have seen these issues, but haven’t read through them too thoroughly. I notice that this same issue was brought up at least once by yourself, and never really addressed:
I haven’t seen any exploration into usability of this trade-off, except for this small blurb (also by yourself):
“Distributed slices” came up multiple times as a "solution” for I have a feeling that this change was made with an understanding that actual cause of the error is not that useful – transient failures are better retried internally and non-transient ones cannot be recovered anyway; this probably makes sense given that various I also saw a mention somewhere that users won’t have to care about errors to the point of knowing that an RNG failed in some way. I disagree with that, and even if I agreed, I do not see any additional value that an error code would bring here. To see why compare these hypothetical, but fairly typical error traces, as one would produce when using With error code
Without error code
|
No, I hadn't heard of distributed slices before, and I remain a little sceptical considering we don't have a portable version yet, but we do have a limited implementation: https://github.com/dtolnay/linkme . @newpavlov's idea is that crates can each add their error messages to a
This was the motivation for the previous design with What type of RNG are you imagining where an error occurs more often than once-in-a-blue-moon? RDRAND already retries in a loop per design spec. The one case of RNG failure I know of was Ultimately, I agree that we should have a solution, and I'm not confident that distributed slices will be available "soon" (@newpavlov?). |
What problem is this actually solving? Your proposed solution (distributed slices) requires either language changes or "linker shenanigans"; what benefit does this actually give to an RNG user when they hit an error? |
I strongly disagree. For end-user having some information about error is better than having none, plus usually you know which RNG you use, so you will be able to check this error code in RNG documentation. This design was heavily influenced by development of
Distributed slices are only needed for nicer error messages, so instead of |
Any errors an Rng might generate are either os errors, including special hardware errors, or stream cipher length errors, yes? There is an interesting research problem of doing a non-locking reentrant PRNG, but ideally such a beast should not produce any error either. In essence, the |
Is usually good enough for a foundational generic interface? If "usually" is good enough, then the whole concept of the EDIT: I do agree that if you do know the concrete RNG type, then it is definitely possible to communicate extra information back to the user. However, if those scenarios were all that we needed to handle, they would be handled equally well by an associated Error type on
Not sure if I would consider hardware errors the same as OS error – hardware isn’t always uniformly abstracted by an OS. |
Your claim was:
I find it's really strange that you find some information exactly equivalent to no information at all. Even in generic context we are able to match on error codes (e.g. As for using associated error type, in my experience they are less convenient to handle in generic contexts. Yes, we could define What exactly do you want to be able to do with |
With what’s available in 0.5.0 I stand by that claim. Some information will exist in an integer, sure, but without means to interpret the number this information provides no knowledge and therefore no value can be derived. And just to reiterate, "By knowing that I’m using crate
So far I’m not seeing either
I’ll be happy if I’m able to:
At the most basic level I’d be content with the aforementioned
but I would also find that to be fairly clunky to work with as far as APIs in Rust go. |
So far our convention has been // —— in getrandom::error ——
// A randomly-chosen 24-bit prefix for our codes
pub(crate) const CODE_PREFIX: u32 = 0x57f4c500;
const CODE_UNKNOWN: u32 = CODE_PREFIX | 0x00;
const CODE_UNAVAILABLE: u32 = CODE_PREFIX | 0x01;
// —— in rand_jitter::error ——
/// All variants have a value of 0x6e530400 = 1850934272 plus a small
/// increment (1 through 5).
pub enum TimerError {
/// No timer available.
NoTimer = 0x6e530401,
... ... and all our defined error codes could be considered "unknown" or "unavailable". We could tweak this and assign meaning to different parts of the code (e.g. 16 bits for a non-zero crate identifier, 8 bits for a "status" code and 8 bits for lib-specific details), then provide functions like But: it's a breaking API change for both To get messages, I think the only options for |
I think @nagisa means that those constants should be duplicated in the
Note that with an enabled |
With this approach One advantage of associated error type would be an ability to use |
IIRC there was some difficulty providing a blanket implementation of the following type due to the lack of specialisation and Rust enforcing no overlapping impls. impl<E: Into<Error>, R: RngCore<E>> RngCore<Error> for R { ... } Thus not only does using an associated type make |
If you need a separate trait, then maybe make the rarely used trait the not object safe one, ala
I've no idea if anyone wants
|
Well, that's the neatest proposal I've heard yet (other than ditching all information in the |
I doubt my second joke proposal actually works since I suppose associated types break trait objects right from the beginning, so you cannot simply hide the methods from the trait object?
|
So I started experimenting with this, and apparently this pub trait RngCore {
fn next_u32(&mut self) -> u32;
fn next_u64(&mut self) -> u64;
fn fill_bytes(&mut self, dest: &mut [u8]);
type Error: Debug;
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error>;
} is Object Safe because to use a fn annoying(rng: &dyn RngCore<Error = SomeErrorType>) { ... } but this is a little annoying, we would like the ablity to use a common impl<R: RngCore> RngCore for R where CoreErr: From<R::Error> {
type Error = CoreErr;
...
} will not work. But we can use a wrapping struct to do the same thing: pub struct Wrap<R>(pub R);
impl<R: RngCore> RngCore for Wrap<R> where CoreErr: From<R::Error> {
type Error = CoreErr;
...
} Then a user can implement non-generic methods like: fn better(rng: &dyn RngCore<Error = CoreError>) { ... } and call them like: let mut rng = FailRng;
annoying(&mut rng);
let mut wrap_rng = Wrap(rng);
better(&mut wrap_rng); Here's a playground with all of the code. |
Yes, by not being object safe I was implying that you can't use it without fixing the error type, thus you essentially lose all advantages of the associated type.This is why for example in RustCrypto I am using a separate Your solution will work, as will a separate object-safe trait, but to me it looks like a needless over-engineering for too little gain. Don't forget that RNG errors will be extremely rare. Do we really need such design complications? Especially considering that in future it's quite likely we will be able to improve user-facing |
I agree, this associated type stuff is probably not worth the hassle. I think that if we can get
Is this just the fixes to |
It's not quite "upcoming", but looks like there is enough interest in it (e.g. see this thread). It's mentioned earlier in this thread "distributed slices" feature, see for example here for a rough draft of how it can look. But even without this feature we can improve situation by adding a private constant array to |
Yes, I similarly meant that associated types must be specified. I'm unsure however if the object safety rules could relax this if associated types never appeared in object safe methods. I do think separating out the
All this sounds like overkill though. |
Agreed, use of an associated type here is overkill. For now, we mostly care about We can approach this in two ways; either hard-code all error-codes and messages in Note that getrandom#54 leaves 2^31 - 2^16 - 1 error codes unassigned; we may wish to reserve some additional blocks (e.g. for Unfortunately this requires changing many error codes from the last release. Landing these changes will already require bumping the minor version on all crates. |
With rust-random/getrandom#58, |
I don't think so. This is why I've insisted on adding the following line to
So we can change error codes and use the highest bit in error code to distinguish OS errors from custom ones. |
This issue has stagnated, yet resolving it (one way or another) is an important step towards stabilisation, thus it deserves some priority. In doing so, we should if possible minimise disruption. Option: add RngCore::Error assoc. typeNo: this is very disruptive since Option: remove RngCore::try_fill_bytesAgain, no; this is also disruptive (though not quite so bad). This also covers use of a companion trait like Option: revert
|
Are we sure feature gated fields in structs really always work? I suppose features always being additive makes them work correctly, yes? It sounds okay then. Are there any Rust WGs that address no_std issues? If so, could this become their problem? I think a WG with wider involvement could do things beyond individual projects, like statically sized manual trait objects, ala
It's true rand could create some manual trait object like this, or some other mechanism, but we're likely better off leaving some no_std WG to do this. |
Like this? We've been using feature-gated fields for a long time already. You mean some type of pre-allocated fixed-size |
I would prefer to not change anything in public API. Firstly, the current approach may be extended in a backwards compatible manner if we'll get distributed slices one day (well, at the cost of bumping MSRV). Secondly, I think *I mean RNG implementations can use code like this: // Private error type
#[cfg(feature=""std)]
struct MyRngError { .. }
fn get_data(code: NonZeroU32) -> rand_core::Error {
#[cfg(feature="std")]
rand_core::Error::new(MyRngError::from_code(code))
#[cfg(not(feature="std"))]
rand_core::Error::from(code)
} |
There are tricks like the one I wrote that do not require language extension pr se, but do require some wider consensus. I'm not suggesting to wait for some WG, but doing something simple now, with a message or two to the relevant WG so that maybe they get interested. |
Lots of good reasoning @newpavlov, though I'd prefer we can find an outcome that everyone (including @nagisa) are happy with. Of course that may not be possible. @burdges your tricks don't achieve much. Type-erasing a reference is easy; storing a type-erased object without a dynamically-sized |
I'm afraid cargo feature passing has proven bug prone, which makes code like proptest-rs/proptest#170 annoying. We might consider methods that either move the crate feature test into rand_core, like exposing impl Error {
#[cfg(feature = "std")]
#[inline(always)]
pub fn make<R>(_code: NonZeroU32, inner: R)
where R: std::error::Error + Send + Sync + 'static
{
Error { inner }
}
#[cfg(not(feature = "std"))]
#[inline(always)]
pub fn make<R>(code: NonZeroU32, _inner: R)
where R: Send + Sync + 'static
{
Error { code }
}
} We should probably ignore this for now in the hopes that some relevant WG blesses some scheme, but I wanted to mention it. |
We got into problems doing that kind of thing before: someone adds the |
Are you saying that you prefer the proptest-rs/proptest#170 approach so that any desyncronization of the std flag causes build failures because the build should fail whenever that happens? I do not disagree, but.. If you wanted builds not to detect the std flag missmatch, then you'd want to minimize the std check appearing in other crates, no? I'd think this requires either exposing |
|
This has stagnated for a long time now, so I am closing with no change (see @newpavlov's last comment, which I agreed to). |
The
rand_core:0.5.0::Error
is reduced down to an integer code, which has exactly as much value as no error at all. The stored integer carries no information whatsoever (esp. in generic contexts) and the error, ifDisplay
ed conveys no useful information either.Consider for example:
With that in mind, the next version should reconsider this API change.
The text was updated successfully, but these errors were encountered: