-
Notifications
You must be signed in to change notification settings - Fork 211
Allow external crates to define backends without RUSTFLAGS
#670
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
Allow external crates to define backends without RUSTFLAGS
#670
Conversation
Thanks so much for looking into this @bushrat011899, I've opened #671 as an issue to track ergonomics around alternative backends. I'll try to take a look at this later this week to see if this would be an option for 0.3 or something we would want to do for a future major version. |
On the first glance the proposed approach looks really messy. IIUC you effectively just expand the |
Yes, that's a good summary of the technique here. Part of the messiness I want to attribute to this being my first time actually looking at I do think this trait-based approach is a direct improvement over the current
Agreed, we can make the default experience nicer by making the
Agreed that exposing extra symbols isn't desirable. Perhaps I could refactor the technique to store a static V-table, so that only a single symbol is exported? This might have a negative effect on performance due to indirection and reduced possibility of LTO though. As for the linking errors, I agree, they're painful to diagnose and resolve. But, this technique allows HALs to test and provide their own backend without user intervention (via This is speculation, but I also imagine it would be more likely for Rust to get better support for diagnostic warnings and custom linking errors messages than it would be to get something like binary-only features. |
For historic reasons Overall, I don't think that the |
@newpavlov coming in as an outsider, it's really jarring for me when I'm developing a game with Bevy, unknowingly pull in a dependency that uses [target.wasm32-unknown-unknown]
rustflags = [
"--cfg",
"getrandom_backend=\"wasm_js\"",
] If I'm collaborating with others, I always need to teach them this hack. Checking it into git in Another issue is that requiring I understand that the current solution is very reasonable for direct dependents, but it's also source of repeated friction and confusion for transients dependents. I also agree that making cargo support these kinds of flags would be the best solution, but historically, cargo moves very slowly with these kind of feature requests. At least, in my experience. Do you think you could reconsider your stance? Or is there a change that can be made to this PR so that you would accept it? :) |
just chiming in that crates who rely on getrandom 0.2 and migrate to getrandom 0.3 (uuid for example) now introduce a semver-incompatible change breaking old builds for no good reason. we've been just pinning rand to 0.8 and not think about this problem, but more and more of the ecosystem is migrating to rand 0.9 and more and more of our builds stop working. As a maintainer of a large project, having to explain to users that they need to start mucking with rustflags just to get random numbers in their web apps is a tough sell. Every papercut counts. Do you need someone with expertise here to fix the implementation? I'm more than happy to put some time into fixing the underlying issue, with or without traits. We want to fund cargo to get global non-unified features in the long run, but that would be a 6-12 month fix when I think with enough thought we could get a 2 week fix. |
Closing in favour of #672, which based on feedback here is much more likely to be actionable. |
I answered to the comments here. |
Background
getrandom
is a foundational crate with a high level of importance across the entire Rust ecosystem. Unfortunately, the ergonomics around using this crate on fringe platforms within a library ecosystem is sub-par. Even more unfortunately, "fringe" includes Wasm in the browser, an otherwise excellent platform for Rust.As an outsider to the project, I feel the same frustrations that have already been litigated in #658, but I also appreciate that this crate must walk a very fine line between performance, ergonomics, and security.
I've had a bit of experience in the embedded Rust ecosystem, where problems like this are quite prevalent (since there's no
std
to rely on for common APIs). One exemplar from that space iscritical-section
, which has a similar problem to solve: platform dependent backend which can only be declared once (globally) and a universal frontend. To achieve this, they use anImpl
trait to describe a backend implementation, and a macroset_impl!
which exports the implementation usingextern "Rust"
functions.This technique has the benefit of moving the "a backend must be declared exactly once" problem into the linker, which already has such a requirement.
Objective
I'd like to demonstrate a prototype of
getrandom
which puts a larger design emphasis on external backends using thecritical-section
technique. Since a backend could only be declared exactly once, it is not possible for a rogue dependency to "sneak" an implementation in (similar to the currentcustom
backend).Solution
Backend
trait which contains all methods which would previously be internally defined (e.g.,
fill_inner,
inner_u32`, etc.).Backend
,ExternBackend
, which defers all its methods toextern "Rust"
functions.set_backend!
, which exports a type implementingBackend
asextern "Rust"
functions compatible withExternBackend
.ExternBackend
in the implementation of the public-facing API.Backend
on a unit struct, and then callset_backend!
on said struct.Error
type to allow descriptions to be declared inBackend
, allowing external backends to provide rich error messages.default-backends
, which enables the current default backends. This allowsgetrandom
to act like a core library crate, andgetrandom = { features = ["default-backends"] }
to be the consumer-facing one. This could also be achieved by splittinggetrandom
intogetrandom_core
andgetrandom
, but that's a much more controversial change.compile_error!
. This allows another crate in the dependency graph to provide aBackend
implementation if and only if no other backend is declared. As previously stated, any attempts to "override" the backend will fail to link.Notes
I haven't removed any of the current requirements for
RUSTFLAGS
usage for things likewasm_js
. The goal of this change is to instead allow a 3rd party crate to cleanly be included by the user (or a relevant library such as Bevy, HAL, etc.) and provide the backend implementation. Since this is offloaded to a 3rd party dependency, it is substantially harder for a misconfigured library to break Wasm (non-web) compilation by simply activating a feature. Instead, they must bring in a whole extra dependency. This is also a benefit for libraries, as they can simply add such an implementation todev-dependencies
for CI.I'm opening this PR in draft to highlight that I don't believe this is ready to just merge as-is; I want to start a discussion on practical changes that could be made to benefit ergonomics without sacrificing the other critical focus areas of the project.