Skip to content

std feature of rand_core changes API #738

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
Nemo157 opened this issue Feb 21, 2019 · 13 comments · Fixed by #788
Closed

std feature of rand_core changes API #738

Nemo157 opened this issue Feb 21, 2019 · 13 comments · Fixed by #788

Comments

@Nemo157
Copy link

Nemo157 commented Feb 21, 2019

Specifically if you are calling rand_core::Error::with_cause with something that doesn't implement std::error::Error (because you don't directly rely on std) then another dependency activates the rand_core/std feature you will get a compilation error.

Simplest way to see this is to use rand as the library that is built on rand_core without the std feature, then activate the std feature in an empty library that depends on both:

[dependencies]
rand = { version = "0.6.5", default-features = false }
rand_core = { version = "0.4.0", default-features = false, features = ["std"] }

This will fail to build with the error:

error[E0277]: the trait bound `error::TimerError: std::error::Error` is not satisfied
  --> /home/wim/.cargo/registry/src/github.heygears.com-1ecc6299db9ec823/rand_jitter-0.1.3/src/error.rs:62:9
   |
62 |         Error::with_cause(ErrorKind::Unavailable,
   |         ^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `error::TimerError`
   |
   = note: required because of the requirements on the impl of `core::convert::From<error::TimerError>` for `alloc::boxed::Box<(dyn std::error::Error + core::marker::Send + core::marker::Sync + 'static)>`
   = note: required because of the requirements on the impl of `core::convert::Into<alloc::boxed::Box<(dyn std::error::Error + core::marker::Send + core::marker::Sync + 'static)>>` for `error::TimerError`
   = note: required by `rand_core::Error::with_cause`
@Nemo157
Copy link
Author

Nemo157 commented Feb 21, 2019

It seems to me that the rand_core::Error::with_cause function should just not be provided at all without std if its constraints can't be supported, it makes using it a little more complicated in libraries that want to optionally support std, but isn't absolutely terrible:

impl From<TimerError> for Error {
    fn from(err: TimerError) -> Error {
        // Timer check is already quite permissive of failures so we don't
        // expect false-positive failures, i.e. any error is irrecoverable.
        #[cfg(feature = "std")] {
            Error::with_cause(ErrorKind::Unavailable, "timer jitter failed basic quality tests", err)
        }
        #[cfg(not(feature = "std"))] {
            Error::new(ErrorKind::Unavailable, "timer jitter failed basic quality tests")
        }
    }
}

Nemo157 added a commit to Nemo157/embrio-rs that referenced this issue Feb 21, 2019
For some reason `--all-features` causes `rand v0.5.6` to be built without `std` against a `rand-core` with `std`, which fails because of rust-random/rand#738

Just force `rand v0.5.6` to be built with `std` during docs build.
@dhardy
Copy link
Member

dhardy commented Feb 21, 2019

Hmm, I'm not quite sure what to do about this.

We have a much simpler design for Error in the getrandom lib now; we could simply adopt that into rand_core (though it's a breaking change).

What cause did you want to include here? Is there good reason to chain a cause, over simply to use Error::new?

I regret that we have two different versions of Error depending on whether std is used, so simplifying the whole seems attractive, but needs more looking into.

@Nemo157
Copy link
Author

Nemo157 commented Feb 21, 2019

I don't have any usecases for this myself, I opened this issue because I have a failing build from this situation somehow occurring via my dependencies, even though I'm not directly using rand crates at all.

@dhardy
Copy link
Member

dhardy commented Feb 22, 2019

Then simpler may be better — unfortunately for you, I don't think any change here will happen soon.

@johansten
Copy link

I'm in the same spot as Nemo157, w/ compilation errors in rand_jitter, from a completely different crate where I'm not even using any randomization.

¯\_(ツ)_/¯

@dhardy
Copy link
Member

dhardy commented Mar 2, 2019

Both rand_core and rand_jitter are optionally no_std compatible, but they must both have std enabled or both disabled. The error given above means that std was enabled in rand_core but not rand_jitter.

Which version(s) of which rand crates are you depending on directly?

@newpavlov
Copy link
Member

newpavlov commented Apr 9, 2019

How about using the following approach for new rand_core::Error?

struct ErrorCode(NonZerou32);

pub struct Error {
    #[cfg(not(feature="std"))]
    code: ErrorCode,
    #[cfg(feature="std")]
    cause: Box<StdError + Send + Sync>>,
}

impl Error {
    fn with_code(code: NonZerou32) -> Self {
        let code = ErrorCode(code);
        #[cfg(not(feature="std"))] {
            Self { code }
        }
        #[cfg(feature="std")] {
            Self { cause: Box::new(code) }
        }
    }
    
    #[cfg(feature="std")]
    fn with_cause(cause: Box<StdError + Send + Sync>>) {
        Self { cause }
    }
}

#[cfg(feature="std")]
impl StdError for ErrorCode { .. }
#[cfg(feature="std")]
impl StdError for Error { .. }

@dhardy thoughts?

@dhardy
Copy link
Member

dhardy commented Apr 10, 2019

Why would you box a NonZeroU32? I presume this is a typo?

Do we still have a reason to chain causes? I guess it might make for better error messages from JitterRng — though keeping the msg: &'static str field may be enough. Are there other important error emitters besides OsRng and JitterRng?

Is this going to cause big issues with backwards compatibility? I guess we can deprecate the kind field (if deprecations apply to fields), with_cause method and take_cause, which can simply return None now.

@newpavlov
Copy link
Member

No, it's not a typo, with enabled std feature Error will contain only Box field.

I think one important use-case for boxed errors is external hardware RNGs. Plus if you unconditionally enable std you will be able to downcast boxed error to get more information about failure if needed.

I don't think it will cause big issues with backwards compatibility, as I doubt that there are many users who check rand_core errors right now.

@withoutboats
Copy link

Both rand_core and rand_jitter are optionally no_std compatible, but they must both have std enabled or both disabled. The error given above means that std was enabled in rand_core but not rand_jitter.

This is not a correct use of cargo features. cargo features are intended to be strictly additive; a library cannot expect that its reverse dependencies will have features turned on when it has a feature turned on.

I cannot build my dependencies because of how they depend on rand. I am now investigating what I have to do to make my build work correctly, even though the code I'm writing has nothing to do with the rand_jitter crate at all. Please fix this bug; the current design is not correct and has a harmful impact on the ecosystem as a whole.

@dhardy
Copy link
Member

dhardy commented Apr 30, 2019

This will be fixed in the next Rand version, but possibly not soon (it could easily be a month or two away).

@burdges
Copy link
Contributor

burdges commented Apr 30, 2019

I've maybe bumped into this. It's caused by slicing the crates up too small I suppose?

@dhardy
Copy link
Member

dhardy commented May 1, 2019

You could put it that way, or you could say that Cargo really needs whole-build feature flags.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants