Rustc_bootstrap = 1

In trying to fix a broken Nix package that depends on nightly Rust, I see that a number of Nix packages use RUSTC_BOOTSTRAP = 1; to enable nightly features.

What are the most important things to know about this approach?

The fact that use of stable Rust binary doesn't bring stability without stagnation guarantees into nightly world. You still can (and would!) be bitten by changes in nightly features.

That, in turn, means that you should use one, particular version of Rust for your packages and version upgrade process shouldn't be automatic.

This being said use of stable toolchain with RUSTC_BOOTSTRAP = 1 is still better then use of nightly toolchain for the distributions because in that case you can expect breakage once per six weeks on predictable schedule, not at any day without warning.

2 Likes

So a Nix derivation with a pinned stable Rust version and RUSTC_BOOTSTRAP = 1 is probably the way to go, in this case?

Edit: though a pinned nightly would be no worse, I guess. (Nix is all about nailing down versions, after all.)

The problem with pinned nightly is that it's harder to cooperate.

Nightly is nightly, it's updated every day and you don't know when new version would be released.

And it's not guaranteed to be available/buildable on all platforms, either.

With stable you at least can be sure that compiler which you need to use (some particular Rust release) would always be available.

Also: rust compiler is not a small package, you probably don't want to download dozen versions for dozen rust apps with pinned versions of the compiler.

But yes, quality-wise stable rust toolchain is, more-or-less, the same as any nightly when RUSTC_BOOTSTRAP = 1 is used.

Using RUSTC_BOOTSTRAP when you aren't working on the compiler is the equivalent of saying,

I really care about stability so I must use a stable compiler buuttttt... I also want to use this unstable API so I'm going to enable unstable APIs, anyway.

If a package depends on nightly features, it should probably be using a nightly compiler.

In most cases, it's enough to remove the #[feature(...)] annotation from the package because the functionality was stabilised since the package was last updated.

Yes. And that's what lots of projects need. That's what Firefox does, that's what Linux kernel does and so on.

They don't need the whole soup of nightly features, they only need small number of them, but these are not available on stable without RUSTC_BOOTSTRAP.

They are usually ready to deal with breakages in that 1% of the code where nightly features are needed, but they want the rest of the code to be stable.

And please don't try to sing tune “if you use just a tiny amount of unstable features in small corner of your codebase then you put the whole thing in the jeopardy”: if that were true then Rust would be impossible to use at all because std uses, literally, tons of unstable features.

If that works, then yes, it's the best option, of course. The question is about the other cases.

AFAIK it's not officially supported, just a hack that takes advantage of compiler's implementation detail.

In practice if you ensure it's always used with the same rustc version, it should be fine. But be aware that this is a hack, and there's no guarantee that it will continue to work with any future Rust version. You are opting out of language stability and compatibility guarantees.

You may find yourself needing to upgrade some dependencies (maybe because of security or other important bugfixes) AND some dependencies suddenly requiring a newer Rust version AND that newer Rust version dropping whatever nightly feature you used or even dropping the RUSTC_BOOTSTRAP hack entirely.

7 Likes

That one is highly unlikely. As long as both Firefox and Linux kernel use that hack (and they both do today) it would definitely stay available. I think even one of them would be enough to keep it from breaking.

The rest of the explanation is correct. In theory you may end up in a situation where two crates would become completely incompatible, but then use of any nightly compiler carries that risk, too.

"Unstable" doesn't refer to how well a feature works, only that it's allowed to make breaking changes. The jeopardy is that the code you write today may not even compile at all with the next release. The standard library is released in lockstep with the compiler, so it gets fixed at the same time when there are breaking changes in the compiler, and vice versa as the compiler uses unstable std features!

3 Likes

I think a better solution to RUSTC_BOOTSTRAP=1 would be a crate similar to standback (a neat crate for using new APIs in std on old compilers), but instead implementing nightly APIs (so you can use future APIs in the current compiler), and the crate can deprecate the APIs that don't make it.

I've thought about starting such a crate myself, but haven't acted on it yet or checked if something similar exists. Although this doesn't fix the problem of non-std changes that the projects may depend on (but proc macros can probably do something similar for some of it).

For what it's worth standback is de facto deprecated because no one used it but me (and I don't any more). There are also some notable issues with the approach looking forward — the compiler's current inference isn't great when it comes to new inherent methods. This is what's preventing stabilization of some methods from itertools already. While version checking could be used similar to standback, that would necessarily require the user to upgrade the dependency and the rustc version at the same time.

I didn't realize it was deprecated. I don't think this hypothetical crate would need to work exactly the same as standback. While non-ideal type inference would be an issue for stabilization in std, I think it would be acceptable for this crate idea. In all reality the implementations of these unstable functions are usable in stable already using the same crates std depends on, so it could more or less re-export items from itertools, futures, hashbrown etc. to look like a hypothetical future version std that might be slightly less ergonomic than what's eventually stabilized. It could still be very useful for some other functionality, at least temporarily until some methods are stabilized. Recently, I've been finding myself re-implementing nightly methods within a crate for use on stable (but they only exist in nightly std, and aren't broken out into their own crate that I could pull in).

Rust for Linux and Firefox code is going to break in newer versions of Rust anyway when these experimental features are stabilized or removed. I've already seen people complaining that Rust is unstable and changing too fast, because they can't even compile Firefox. The env var could be thought of as just one of the nightly features subject to change at any time.

If Rust starts caring about not breaking users of unstable/private/nightly features, this will effectively stabilize the these features. That makes the distinction between stable and unstable blurry, and adds risk to Rust adding any nightly features. It's not a good place to be in. The Web platform had this issue with -webkit vendor prefixes in CSS. The prefixes were supposed to be temporary, vendor-specific, and experimental, but high-profile websites started using -webkit properties, and these properties had to be adopted by other browsers and ended up part of the CSS standard.

5 Likes