Why is `RUSTC_BOOTSTRAP` so actively discouraged?

My thought process is as follows:

  1. Many people with more experience than me have thought about this more than I have, so I'm probably missing something ...
  2. Rust has very strict and reliable stability guarantees for very good reasons. This does, however, mean that it can take a very long time for very useful features to become fully stabilised. For example ! has been ongoing since 2016; try_v2 since 2021; assert_matches was in progress from 2021 until 2026. It's understandable that true stabilisation takes years for big complex topics & changes - this is in no way a complaint.
  3. As a developer I have three options:
    1. don't use new features which would be really helpful and wait a few years.
    2. use nightly
    3. use stable with RUSTC_BOOTSTRAP

Why I personally tend towards choosing option 3:

  • I don't want to use nightly as I don't want the instability of daily changes and the risk of breakage at any time.
  • I can trust that stable will not accidentally break something given the wonderful nightly-beta-stable release process that's in place. That means my only risk is of a deliberate breaking change in an unstable feature I've chosen to use. That's a risk I can better estimate and weigh against the value of using that feature.
  • I can set up automation to test my crate with each new release and take action on failure (although to my knowledge I have no way of enforcing a maximum supported rust version).
  • Running automated checks daily is a much bigger use of resources, both natural and personal. I can plan better around a quick check every 6 weeks.
  • I'm effectively forced to have RUSTC_BOOTSTRAP set on my local machine while coding if I want to have rust-analyzer support for testing and linting without huge delays every time I run a test. See: Issue #17149 · rust-lang/rust-analyzer

To my mind that means I get much more limited instability at zero cost vs nightly and I can choose to opt in to a feature where I believe it is worth the cost of instability in that specific feature

So why is this approach so actively discouraged?

The last discussions I can find are from 2021 here & here; they are focussed on slightly different use cases and revolve around making additional changes to the language while taking "RUSTC_BOOTSTRAP is bad" as a given.

2 Likes

You can pin to specific nightlies with rustup.

9 Likes

For a library RUSTC_BOOTSTRAP is a really bad idea. If effectively forces all your consumers to risk being broken with any new rust release. Builds of Firefox broke once because one of their dependencies used RUSTC_BOOTSTRAP internally. After that incident Cargo got changed to not allow individual crates to opt in to RUSTC_BOOTSTRAP and instead force the person invoking cargo to pass it and thereby acknowledge that the project may break with rustc updated. Also if some library deep down uses RUSTC_BOOTSTRAP then it may be very hard to ensure this library gets updated. The library may not do a timely release (for example because it is not actively maintained) or it may do a new release, but the new release is not semver compatible with the version that your project uses. In both cases you are effectively forced to vendor and patch the library. Also the library update may (have to) break compatibility with the MSRV of the project. And finally if the library only updates for breaking changes to unstable features whenever a new stable version gets released, that makes it effectively unusable on nightly and beta, preventing your users from finding bugs in rustc before it is already too late.

For an end project, these risks are less bad and there are some major projects that use it (Rust for Linux (RfL), Chromium, ...), but still we prefer if people don't do this. In the case of RfL we are actively collaborating on unstable features they need to get them to a stabilizeable state. And for RfL we have a CI job on the rust side that allows us to notify the RfL maintainers about the impending breaking change such that they can adapt to the changes quickly.

10 Likes

I fully understand and agree with the sentiment that any use of unstable features in a library should be very carefully considered and brings significant additional responsibility to the author and risk to the user. (That's option 1 on the list - "don't use the feature")

What I don't get is why stable + bootstrap is "worse than nightly" and not "better than nightly" in this regard.

Maybe it's a communication thing and the idea is to avoid any hint of "using unstable features is fine and safe and stable" (?) so as not to end up with a large part of the ecosystem full of crates which overheard the nuanced "carefully weigh the costs and risks when deciding". "Nightly only" with a big yellow warning is scary (deliberately, and for good reason).

Yes, but why does that argument not hold just the same for nightly (also not available on a per-crate basis) where the breakage could happen every 24 hours, not every 6 weeks?

All valid risks whether the library uses stable, nightly or bootstrap. Although the delineation is admittedly very clear between stable (if the library author ignores this forever, rust authors guarantee it won't break between editions) and nightly (you need to trust the library author to keep on top of this constantly).

That I don't understand. I can cargo +nightly/+beta build/test such a library or anything that uses it. I have the risk that something broke in the specific enabled feature in the last few weeks which the author hasn't seen or reacted to yet, but if I'm actively choosing nightly or beta then I am deliberately buying in to higher risk for reasons which make sense to me.

Or a bug in an unstable feature, which will stick around for a full stable cycle even if it's been fixed in nightly, because people are not necessarily paying attention to whether a given unstable feature remains functional in a stable release. Any given nightly feature can be broken in any given nightly, and fixed in a subsequent nightly; if that happens to be the point at which beta branches from, the feature will stay broken, and there's no particular reason to backport a fix for it.

11 Likes

First of all, it is your choice how often you update the nightly version you use or support for your project. Nobody forces you to update every 24 hours.

Also, if you use stable, then things are very unlikely to break every 6 weeks. We spend a lot of effort on that. But if any of your dependencies uses unstable features, the risk of breakage increases dramatically. Therefore it is very important that you as application developer know whether any transitive dependency needs unstable features. But anyway that hole in the Rust stability story has been fixed so this is not a concern any more.


To answer the original question -- there is a non-trivial social contract around RUSTC_BOOTSTRAP. Using it responsibly comes with a bunch of obligations to the party that enables this flag. If it gets overused, that is very likely to do enormous damage to the Rust ecosystem. We need the freedom to iterate on unstable features, including breaking changes, without the overhead of deprecation periods and future-compatibility cycles. If a sufficiently large part of the ecosystem breaks when we do that, we effectively lose that freedom, which is very bad news for Rust language development. We strongly discourage its use because we want to avoid that damage.

So, why should you not use RUSTC_BOOTSTRAP? For the same reason that you shouldn't let your cows overfeed on the common pasture.

18 Likes

Thanks for the clear and honest explanation. I'm glad I hadn't missed anything technical and had simply mis-considered the social impact.

Regarding how often I update my nightly - that's less of my concern than how often my downstream users will (and over that I have no control, so must assume that every nightly version could be used immediately)

While likely rare occurrences, I see that more acceptance of RUSTC_BOOTSTRAP would lead to pressure on maintainers to backport breakages into beta as per josh's comment above

Most importantly: I see the risk to the ecosystem if RUSTC_BOOTSTRAP started down the slippery slope of not-shunned->accepted->normalised as it could easily become very widespread and feel a lot safer than ":warning: nightly", leading to many broken crates. I will certainly ensure I document my work with this in mind (I'll be updating the readme/crate-level docs and publishing a hotfix release to at least one crate today [update: done, all references to bootstrap removed for try_v2 & currently #200 in the queue at docs.rs]).

Following on from this, I'm wondering what the right wording would be for:

Stability

This crate makes use of unstable features x,y,z (with links), as such it requires users to set RUSTC_BOOTSTRAP=1on stable, or use nightly.

The crate is tested with RUSTC_BOOTSTRAP = 1 on each update of stable (every 6 weeks). In the unlikely case of breakage, a hotfix version update restricting the supported toolchain will be released immediately and a fix will follow as soon as possible afterwards.

As such you can expect the crate to work as long as features x,y,z are not broken in the current stable/nightly build.

maybe something like:

Stability

This crate makes use of unstable features x,y,z (with links). Great care is taken to ensure continued stability of the crate and the use of unstable functionality is strictly limited.

Automated tests are run every 3 weeks, against the version of nightly which is promoted to beta and again when a new stable is release. In the unlikely case of breakage, a hotfix version update restricting the supported toolchain will be released immediately and a fix will follow as soon as possible afterwards.

In case of issues with a specific nightly release outside these checkpoints, please raise an issue (link) including details of the toolchain used.

Is there already something out there in common use that I've just never noticed, analog the standard inclusion of a "Safety" section for any usage of unsafe, and the function-level annotations for nightly APIs in docs?

FWIW, I think the issue mentioned in rust-analyzer is the kind of thing that does harm via propagating RUSTC_BOOTSTRAP. rust-analyzer apparently said "there's an unstable JSON output that we want, so we're going to set RUSTC_BOOTSTRAP to get it", and then a predictable set of problems ensued. This should never have happened in the first place; stable rust-analyzer should not be using this feature.

9 Likes

yeah, (a bit off topic but a good example of the issues involved) that's an almost impossible tradeoff, every choice is bad:

  • have poor support of in IDE testing for a major IDE where it is generally expected -> poor DevEx for the language
  • make (undocumented) use of RUSTC_BOOTSTRAP and have users annoyed about why their machine keeps completely rebuilding the project before and after every test run -> poor DevEx for the language
  • clearly document the use of RUSTC_BOOTSTRAP, propagate acceptance and ... see above points made by Ralf -> poor DevEx for the language

Part of the issue is that using RUSTC_BOOTSTRAP sidesteps the process of "finish and ship a stable feature".

2 Likes

automation can help a lot with this. My standard setup is to have dependabot run daily with auto-approval on all PRs where nothing breaks.

This only works because I have meaningful, complete test coverage (that's how I code out of preference). Still, if a downstream dependency makes a breaking change I have to go fix it (one crate in particular does this often for something I care about) and yes, that's often a hassle and very easy to overlook for longer than I feel comfortable with. But at least I can rely on cargo to ensure no-one else gets broken, "just" held back.

This isn't really true for unstable features, they're commonly undertested and have bugs which will reach a stable compiler, and as already mentioned you then need to wait 6+ weeks till the next release to see if it fixes it. Whereas if you were using a pinned nightly you can report the bug and maybe it'll be fixed sooner and you could update your pin.

But really the main reason is that you must take all responsibility of anything that breaks if you use RUSTC_BOOTSTRAP, if it becomes normalized to use it then people that don't take that responsibility will start using it and complaining when stuff breaks. Increasing the workload on contributors for no real gain.

1 Like

While it hasn't seen much recent motion, there is an RFC that proposes both breaking down RUSTC_BOOTSTRAP into finer grained control, and discouraging that replacement feature less:

5 Likes

Argument from second-order effects:
Doesn't it depend on bugs of unstable features being reported from the wild? For that, you'd want people trying out the feature as soon as it appears (which is in the nightly).

Personally speaking, whenever I try to use a QoL feature in my app and see it is unstable, I just switch to nightly to never come back to stable.

The OP does correctly mention the merits of versioned nightly.

Indeed, I do think version nightly, assuming it's interpreted as x.y.z-nightly in Cargo-Semver parlance (i.e., breaking even when just bumping z), can be preferable to "dated nightly", barring patches:

  • version numbers are way more memorable than arbitrary cut-off dates,

  • especially when cross-referencing with stabilized APIs.

    e.g., do I need a feature to use ::core::fmt::from_fn()?

    • if you were using x.y.z-nightly versioned nightly, then you'd be able to answer this directly: since the cut-off version stabilizing it is 1.93.0 as per the docs, then on 1.92.0-nightly or below you'd need the feature, and 1.93.0-nightly and onwards you would not.

    • but if you're using nightly-yyyy-mm-dd, how do you quickly answer this question for nightly-2025-11-23 vs. nightly-2025-01-03, for instance?

      The answer is that, as a first approximation, you'd need to identify the cut-off date of the 1.93.0 nightly, using, for instance, https://releases.rs, from which we can observe it was December the 5th 2025 for 1.93.0,

      releases.rs

      and October the 24th for 1.92.0; so we know that prior to 2025-10-24 it will for sure be unstable, and 2025-12-05 onwards it will be stable; but for my november date example it remains unclear:

      So the more accurate approach is actually to find the stabilization PR and look at the merge date (not too difficult, but definitely more hassle than "just" going to https://releases.rs, itself more hassle than checking a version number on https://docs.rs/std:

      ezgif-38cf106efcb3683c

      Which gives us the date of November the 4th (for GMT+1); so nightly-2025-11-05 and onwards ought to do it?

:face_with_spiral_eyes:

So yeah, it would be quite handy to get some form of versioned nightly.


And it just turns out that, for now, since we don't have that in any form, the only way to hackily DIY-polyfill such a thing is through RUSTC_BOOTSTRAP=1 +stable.

And this is socially problematic, since it has the name stable in its name (it sounds like "nightlied stable" more than "versioned nightly").

I personally, and at work, use this in certain scenarios (whilst being willing to exceptionally use pinned nightly for a hotfix), and to great success, out of the value of versioned nightly as a concept. But it is very important to be aware of the tradeoffs and limitations that come with it.

TL,DR

We'd probably benefit from an official support of versioned nightly so as to supersede this hacky but otherwise currently-legitimate usage of RUSTC_BOOTSTRAP=1 +stable.

5 Likes

Theoretically this should "just" be a new rustup channel, right? No need to invest extra CI build time. It should be fine if the --version reports the same as the nightly build, so when promoting 1.ZX.0-beta, "just" also push that day's nightly build to a new "fortnightly"[1] release channel.

1.ZX.0-nightly is thus equivalent to the first nightly for 1.ZY.0. This is one release train off of "grab the nightly from the same date as the named stable," but I think fits the best with the existing version trains, since it would ensure that, modulo beta backports, 1.ZX-nightly ≈ 1.ZX-beta ≈ 1.ZX-stable.

In a perfect world with infinite CI build resources, the "versioned nightly" toolchain would be the beta toolchain but built to support unstable features, thus receive beta backports, and could even entirely replace the use of RUSTC_BOOTSTRAP for building rustc[2]. But that isn't realistic, so just an alias channel would be a relatively cheap nicety.

<aside>I typically just pick first-of-the-month nightlies to pin to, unless there's a specific recent nightly with a change I want. (But actually I'm usually on floating nightly since my nightly using projects are usually short lived experiments or only use stable-but-for-X (typically library) features.)</aside>


  1. Fortnightly would be a technically incorrect but cute release channel name. A technically correct but obscure option: sesquimonthly (1½-monthly), sexaweekly (6-weekly), or octantly (⅛-yearly). ↩︎

  2. Though we probably would need to continue supporting some sort of unstable enabling environment variable for IDE usage, like exists for enabling ra-proc-macro-host. IDE plugins are effectively part of the toolchain, able and expected to keep up with nightly churn, and updated at least as often as the toolchain itself, so maybe the best use case of unstable-on-stable after bootstrap… at least if it only effected what the IDE has available and wasn't visible to the built crate (e.g. causing rebuilds). ↩︎

2 Likes

:joy: love this! :clap: (and I agree with/like your more serious suggestions)

1 Like

Thanks. I've thought about that RFC a lot, since you posted, and have finally organised my thoughts well enough to add a comment.

Overall, I'm conflicted on this topic, as I see the danger to stability-as-a-feature from having any experimental feature usage on stable. At the same time, I see, and feel, the clear need to have a more stable release channel where opt-in is possible.

At least now, my original question is well & truly answered.