Description
The feature being requested
Please add a cargo capability to designate from the top level of the build which specific crates in the dependency graph are compiled with RUSTC_BOOTSTRAP=1
even when using a non-nightly compiler
Use cases
A piece of software that depends on many Rust crates (such as Firefox) tracks the stable release channel for the Rust compiler. Yet, in some case, it makes sense to use an unstable rust feature nonetheless.
- There is a temporally substantial (not just a couple of Rust release trains but even years) mismatch between Rust stabilization priorities and the shipping needs of the piece of software that is using Rust code. The most obvious example of this is portable packed SIMD in Firefox.
- A more temporary but immediate need comes up to use a Rust feature that has an active stabilization plan a couple release trains ahead of the Rust stabilization schedule. In the case of Firefox, some allocator-related customizations where of this type.
Note that making the first use case go away by stabilizing packed portable SIMD (which would be great!) does not make this feature request moot, since it seems reasonable to expect that the second kind of situation will keep arising from time to time even if there are periods in between when nightly features aren't needed.
Alternative solutions and what's wrong with them
Just use nightly Rust
The general tendency of Rust seems to be that if you want to use a nightly feature, you should be using nightly Rust. This poses problems:
- If the piece of software using Rust is not only compiled for distribution by its developers but also by Linux and BSD distributions (as Firefox is), it is not scalable for each upstream project to designate its own snapshot of the Rust toolchain for the Linux and BSD distributions to use. In fact, for some distributions even the notion that building Firefox with in-archive compilers requires packaging the (Rust stable-channel) Rust toolchain on a rolling basis is a big departure from how they usually do things. Having upstream software require the packaging of upstream-designated snapshots of nightly Rust would probably not be well received.
- Using nightly Rust means giving up on the value that the Rust release train model is supposed to provide. That is, the Rust release train model is supposed to catch regressions that land in nightly before they reach the stable channel. If there truly is such quality assurance value in Rust's train model, it seems bad to ask projects to give up that value for all code they compile if in a vast amount of code they have a couple of specific things that need nightly-only features.
- Especially given the first point, "just use nightly Rust" does not really work as a temporary solution to a temporary problem (like the allocator-related customization in Firefox), because in the context of downstream packaging, the timescales to plan for such a change (if feasible at all; see the first point) would likely exceed the time period when a given nightly feature is temporarily needed. Notably, "just use nightly Rust
is not just a quick matter of
rustup default nightly` for software that gets packaged downstream.
Additionally, the "just use nighly Rust" solution has the problems of the next solution as well.
Set RUSTC_BOOTSTRAP=1
for the whole build system
This is what Firefox used to do.
The main problem with this solution is that it's very easy to casually introduce more dependencies on nightly Rust features than was intended. Specifically, if nightly features are enabled for specific purpose that's well-known to people who take care of toolchain issues in the project, there is some chance of tracking the status of each nightly feature of interest in Rust development. If, however, developers casually introduce more dependencies on nightly features, soon no one is tracking which nightly features the project as a whole depends on ending there may be unpleasant surprises if nightly feature breaks and the person who casually started using it hasn't been tracking the status of the feature and hasn't been prepared to deal with the nightly feature changing.
In general, to the extent it is some sort of Rust position that production users of Rust should not be relying on nightly features, it seems incongruent with that position to suggest solutions that easily lead production users to depend on arbitrary nightly features when their use case is about depending on a very small number of them.
Notably, unintentionally adding a dependency on a nightly feature is not necessarily a matter of a developer "unintentionally" typing #![feature(foo)]
in the code they are writing: #![feature(foo)]
may be buried in a dependency, and with nightly features being behind cargo features in dependencies, it may be even less obvious what nightly features are enabled.
Use println!("cargo:rustc-env=RUSTC_BOOTSTRAP=1");
from build.rs
This is what Firefox does now.
This hack avoids the problems of the above two solutions by scoping RUSTC_BOOTSTRAP=1
to specific crates. It's own problems fundamentally arise from the hack being syntactically part of an individual crate while the the need for the hack is a configuration concern of the top-level application.
In a non-cargo vendor
scenario, putting application-level configuration in a specific crate basically requires maintaining a git fork the crate with an extra change set just for the configuration and the ceremony of telling cargo to replace the crates.io instance of a crate with the forked git instance. Even in a cargo vendor
situation is not a matter of maintaining a patch that always gets applied after re-running cargo vendor
: cargo requires more ceremony to opt out of hash checking for the vendor crates.
Even if the effort required to maintain application-level configuration as a change to an individual crate is relatively mild, the path of least maintenance burden still is to make the change in the crates.io crate itself. This poses the risk of other applications unknowingly becoming reliant on configuration meant for a different application. This is probably harmless for crates that are blatantly obviously nightly-only, such as previously the simd
crate or presently the packed_simd
crate. It can be problematic, however, on crates that conditionally depend on such crates. For example, encoding_rs
has a cargo feature simd-accel
for enabling the dependency on, depending on encoding_rs
version, simd
or packed_simd
and gaining the associated performance benefits. While it should be obvious that this is a cargo feature as opposed to the default state of things precisely because there is some tradeoff (in this case, trading compatibility with future Rust toolchains for performance today), the full implications of the trade-off might not be obvious and someone might enable simd-accel
with a stable-channel toolchain and be unhappy later when it toolchain update breaks the build.
Preferred solution
My preference for solving this would be adding the ability to use the top-level Cargo.toml
to indicate which crates in the dependency graph are to be compiled with nightly features enabled. While naming is a total bikeshed, I suggest putting something very obvious in the syntax along the lines of enable_features_that_future_rust_releases_may_break
instead of relying on short but less obvious terms like nightly
or bootstrap
.
There is other per-crate configuration tha the top-level Cargo.toml
should be able to control, such as the optimization level. In principle, it would be good for the solution for this feature request to be consistent with other such configuration, but I also wish that this feature request doesn't get deferred indefinitely due to consistency concerns.
Alternative solution
It has been suggested that instead of introducing syntax into the the top-level Cargo.toml
the RUSTC_BOOTSTRAP
environment variable should be extended to take a list of crate names as an alternative to taking the value 1
. While this would address the use case, I consider it better design to recognize the use case and to provide top-level Cargo.toml
syntax (that makes the downsides clear) along with other per-crate configuration than to make RUSTC_BOOTSTRAP
more magic with a special-case design that sort of tries to avoid recognizing the use case by deliberately keeping the mechanism obscure.
Potential downsides
A potential downside is that people use enable_features_that_future_rust_releases_may_break
and are still unhappy when breakage happens. However, the standard approach of telling people to "just use nightly Rust" already has this problem.
The best solution to avoiding unhappiness from changes to features that are explicitly subject to change is to work actively on "subject to change" features so that they actually don't appear de facto unchanging and so that they spend a relatively short time in the "subject to change" state. Fixing that kind of general prioritization issue is outside the scope of this feature request, though.