Code compiles on playground but fails when passed via stdin to rustc

Do note that I implemented this as an undismisable note, which could also be a warning (that wouldn't be affected by forbid warnings, if iirc, as that only looks at lints, but might be getting things wrong there). The only people affected would be those explicitly comparing textual compiler output in scripts.

4 Likes

Around the time we approved an environment variable, there started talk among T-lang to add an edition cfg which would then be exposed to build scripts as CARGO_CFG_EDITION. That went through several proposals, delaying it. The latest has been untouched for enough time that it might not be worth waiting on anymore and to instead use CARGO_PKG_EDITION.

2 Likes

I'm also struggling to see how this would work. To my understanding the cargo env variables are set once for a cargo invocation[1]. Build scripts and edition selection are unique to a specific crate, wherever it sits in the dependency tree; not driven by the one(s) at the top of the chain which triggered the build. Edit: got it now [2] weird how I can spend 2 days trying to figure something out and then get it almost straight away after asking publicly!


  1. building all crates in parallel and the global-nature of env vars suggests this cannot be done any other way(?) ↩︎

  2. prefix the call with the env cars directly ↩︎

Cargo sets a bunch of env vars for each build script invocation with different values depending on the package they are part of.

I wish we had proper cargo support for build probes (or something else that alleviates the need for build probes) so people wouldn't have to write their own hacks to substitute for lack of official support. But, well, everyone only has finite capacity so here we are.

There is an env var you can set which makes rustc -vV claim that this is "stable" even if you are on nightly; this will disable most of this kind of auto-detection (and it's very clearly a bug in the ones that do not get disabled): RUSTC_OVERRIDE_VERSION_STRING.

I'm genuinely confused by what appears to be the prevailing negative view of build probes in principle. (I'm quite able to understand a negative view of poorly-created build probes in practice)

Here's my thinking. What am I missing? Is it another implicit social contract?

  1. No other crate can force you onto nightly or any other version of stable.

    • The toolchain version is decided by whoever is compiling the final binary. It should be up to them and only them what is used.
    • If that's you in this case - how are you being forced to use an experimental nightly feature?
    • If you're a lib in the middle - why should you be able to restrict your downstream user's choice of toolchain?
  2. As a lib developer I may want to provide additional functionality which requires experimental nightly features

    • if this is in the form of extensions to the API, via a feature - the direct consumer needs actively use the additional API, so it makes sense that they set a feature in their dependency (on me)
    • if this is via a consistent API, via a build probe - it doesn't make sense to require a direct consumer lib to make a choice for their downstream users. Should they opt in and only support others on nightly? Or opt out and their users lose the benefit?
  3. The fewer people use nightly experimental features, the longer it takes to get meaningful feedback on them and the slower the language develops.

    • The very real impact of this are year-long timelines for amazing ergonomic changes - that's not due to lack of effort etc. from the various teams, that's simply due to the reality of usage levels and the time needed to get good data.
    • To my mind, taking the time as a lib developer to try to incorporate nightly features in a way which allows stable-by-default to "just work" and gives value add for those who wish to use nightly is the least I can do to support the people who devote huge time and effort into improving the language.

I'm not sure that anything short of well-written probes and thoughtful API design would solve the issue. I've recently tried to lib-ify the probes that I use - and it's not even possible to have a general probe for "do you need this #![feature(...)]" that works in every case.

Perhaps providing a section in the unstable book on these topics would help with reducing the number of "hacks" and increasing the number of well-written probes & APIs?

Which is me, I only care about build probes when they execute on my system (maybe because I'm developing an application, maybe when I'm compiling the tests for my library).

Because I use a nightly compiler to take advantage of unstable toolchain features that don't impact the code, just improve the developer experience e.g. Cargo's workspace-feature-unification. But dependencies see that I am using a nightly compiler and attempt to use other unstable library or compiler features that I don't want them to.

And you still can, you should just provide a way to allow users to opt-in to this (e.g. via --cfg flags or environment variables). If users do not, don't just assume you can use them (since build probes are commonly wrong and cause compilation failures, e.g. latest instance of this I've found).

If I do not know that a dependency of mine has enabled an unstable feature, I am not going to give feedback on it anyway. By surfacing the feature as something the final developer actually enables that gives you a chance to link to the tracking issue and suggest they go leave feedback if they use it.

6 Likes

As the person choosing the toolchain to build with, I may be using nightly for any number of reasons; in particular, I may be using it to get access to flags useful for debugging that aren't stable, or to use a newer compiler version that might have fixed a bug.

When I switch to nightly for one of those reasons, I don't want my project's dependencies to also implicitly change to making use of unstable features. I don't want them to be compiling different code. I don't want them to be exercising parts of the compiler that are marked unstable. I want everything about those dependencies to be exactly the same except what I chose to change (the compiler version) so that the results of my experiment are as controlled as possible.

That said, I would also enjoy having a sort of global feature flag (perhaps just a conventional environment variable, or cfg to stick in RUSTFLAGS?) to tell every dependency "yes, do use all the unstable features you would like to", for the testing of nightly features. But I don't want that to be implied by any usage of nightly at all, which is what happens when libraries use build probes for unstable features.

10 Likes

Beyond the anecdotes given, there is a general principle within the Rust Project that unstable features only impact those who have opted in. We should not be gating functionality based on the channel. This makes it much safer for people to run nightly. The rare exception is when we are needing extra testing on something right before stabilization. Libraries auto-enabling features are running counter to that principle.

2 Likes

That makes perfect sense.

With a small twist I'd agree that that is a great idea ...

Why not the other way around? Instead of requiring an opt-in to a full nightly experience on nightly, provide an opt-in-to-only which then disables everything else. That way those who need or want very granular control have it with no extra effort and those who want "full nightly" also have that with no extra effort.

It's the idea of either requiring an opt-in if you don't want granular control, or, worse, if you don't want an arbitrary set of pre-defined features that irks me.

and there's the rub ... the final developer cannot enable a feature if they are consuming via a transitive dependency.

Yes - you could require them to set a cfg flag or env variable manually ... and some choose that route. Personally, I prefer a crate that documents clearly if they auto-detect & use nightly features to one that makes me go through that hassle, and choose accordingly (just as others will choose the opposite)

I have wondered about replacing nightly cargo features with Pre-RFC: Mutually-excusive, global features . More recently, I found out about -Zallow-features and wonder if there is something there we could build up to better support these use cases, including forwarding that to build scripts and proc-macros for the code they generate.

2 Likes

Sounds like that actually already exists. (Thanks - another thing I learned today!)

So, what's the concern about a build probe which correctly reacts to RUSTFLAGS="-Zallow-features=" by not enabling any nightly features?

If you want to limit what is enabled on nightly for reasons like those above, set the env variable, ...

(I just checked and all my probes react correctly to it - so it's not rocket science, just so under-documented that getting probes right is much harder than it needs to be)

Did you try when set via CARGO_UNSTABLE_ALLOW_FEATURES too? That’s probably the more common interface to the functionality now that it exists, and that’s what I’ve been running into a lot of buggy probe handling for recently.

(And it’s kind of an eternal fact that every time the interface for this sort of thing improves, build scripts will fail to handle it because they’re kind of fail open, instead of fail closed; I remember long ago when none handled RUSTFLAGS correctly either).

Yep - and no that won't work - for the same reason as the original post in this thread. I'd consider that a different issue though and question whether CARGO_UNSTABLE_ALLOW_FEATURES should remain the more common approach given -Zallow-features now exists, offers the same and doesn't suffer from the problem of "only relevant when rustc is invoked via cargo" (or whether cargo could set -Zallow-features based on this setting)

If I understand you correctly - that's bad build script design. (I'd expect a build script to see "is rustc[1] actually going to be able to compile this code in the current environment")


  1. rustc, not cargo. Although I'd see nothing in principle against cargo developing an equivalent set up support functionality. In practice it's a lot of work for something very controversial ↩︎

It's the other way around, rustc -Zallow-features has existed for years longer than cargo -Zallow-features (but I'm pretty sure that is also years old at this point). The latter is a superset of the former, it controls both cargo and rustc unstable features, and has a much better UX for shared workspace definitions in its TOML form.

Basically every build probe is broken in subtle ways. It's not currently possible to construct a correct build probe, cargo does not provide enough information.

autocfg is about as good as it gets, but a lot of crates roll their own build probes and many of them are ways worse. By asking about "a correct build probe" you are asking about a hypothetical alternative universe. In practice, a large fraction of build probes are broken.

And even perfect build probes still have the wrong polarity: nightly features should be opt-in, not opt-out. That's how the entire Rust nightly feature system is designed. I shouldn't have to set an env var that says "please actually give me a reliable developer experience where I can update my compiler without breaking the build". Defaulting to "unreliable developer experience" is a terrible default. It is frustrating to see that principle constantly violated by a small set of crates, to the detriment of nightly users and compiler maintainers that want to do normal nightly feature development (i.e., change/break how nightly feature work) or that need to debug regressions.

I understand that there's an actual need here from the side of crate developers. But instead of running ahead with a broken non-solution, I would have hoped there would be some attempt at doing something standardized that actually works for everyone involved. Sadly, to my knowledge, that was never even attempted. Now my best hope is that we do get global features in cargo and we can standardize on a global feature to control whether crates should use nightly features that they don't strictly need.

6 Likes

I realise that my previous comments can easily appear argumentative (and have thought a lot about this since my last post here). I'm sorry for that, it was not my intention.

I think I've understood the complex web of intertwined concerns, needs & historical frustration - here's my attempt to summarise it

Groups using nightly

There are two, relevant, groups of rust users who have very different expectations and needs when they use nightly:

Group A - safety is a requirement

These are people using rust because the safety guarantees provide safety(!). Obvious members are regulated industries where failure can cost human lives; rust for linux, ... I get the feeling that this is either where you are @Nemo157, or at least closely related to your mindset.

Every use of nightly requires a clear risk assessment.

  • Developers may need to use nightly to allow debugging support etc. - they should be able to guarantee that their local work is otherwise identical to the final built product.
  • A small, reviewed, whitelisted subset of experimental features may be required to make the product work - each of these is well considered, documented & risk-assessed.
  • Build scripts are needed to support product-specific technical requirements.

This group really needs the ability to work on a nightly toolchain and have everything act just like stable except where specifically whitelisted (whether developer or product related)

Group B - safety is an ergonomic benefit

These are people using rust because the safety guarantees make coding nicer/easier/more ergonomic. These are the evangelistic colleagues who grab you at the coffee machine and say "let me show you rust". I'd consider myself in this category.

Channel selection is a mainly binary decision: nightly or stable?

  • sticking to stable can be frustrating - so many ergonomic improvements are available on nightly for a very long time before they stabilise.
  • using nightly is viable for binaries but colours libs.
  • the lib eco-system generally avoids nightly features, even where these could provide a more ergonomic API.
  • build scripts provide a way to remove the colouring and support stable & nightly.

Technical blockers

  • cargo does not provide details of unstable.allow-features to build scripts (+ other settings?) & has previously been against extending build-script-specific support
  • there is effectively no documentation of "how to write a good build script"
  • there is no documentation on how to probe for the availability of a nightly feature
  • no specific probes are available from trusted sources

Facits

For "Group A" this is at the granularity of "I opted in to only these specific features". For "Group B" it is "I opted in by using nightly".

That means someone is always going to be upset. As long as there are different groups with different needs (& usually only a few members who consider the other groups may even exist - I'd consider all the participants here to be the "considerate" category BTW) then each build script author needs to decide how to handle these edge cases and given that many come from "Group B" that means usually

Thoughts, ideas & opinions

  1. Build probes are here to stay - as much as they cause problems. (no value judgement, just raw practical observation, which has been stated by others who are active in this thread too)
  2. Perhaps providing (edit) CARGO_UNSTABLE_FLAGS (probably outputting it (edit: from the GlobalContext) here - based on a first reading of the code) would be better than not?
  3. Documenting best practices & providing well-thought-out probes would reduce the average bugginess. The cost is "implicit approval", the cost of status-quo is "constantly broken probes".
  4. Thank you for reminding me of the needs of "Group A". I will add a "--cfg namespace=stable" to all my nightly-testing build scripts (via my personal lib for consistency). At least that way people can fully opt out of the probes. I target mainly "Group B" types so a "must opt in" is not suitable for the main target audience. I know this is not what most on this thread would consider "ideal" or "correct" - but would it at least be "enough" @Nemo157?

My case is about stability rather than safety. We use nightly for a few features we believe are worth the cost of lower stability, we have looked at the tradeoff of not using them vs. the chance of them causing issues and how we would remove our usage. We want to be able to very freely update our compiler without having to worry if dependencies will break.

2 Likes

Got it, I see the subtle difference. thanks for the clarification.

OK ... so I had one of those bad brain days after the dog woke me up at 4:30 this morning. And I finally gave up after lunch and just let ADHD-brain choose what to focus on for the afternoon.

So - challenge accepted (it just felt interesting and impossible is always motivator)

I think I have a solution with:

  • a set of truly safe build probes (for a few select features)
  • which follows the opt-in approach of optional whitelisting that rustc & cargo both use (opt-in 1 to unstable, then opt-in 2 to granular opt-ins, then opt-in 3 to individual features)
  • respecting cargo unstable.allow-features & RUSTFLAGS="-Zallow-features"
  • in a way which promotes good practices and makes short-cuts very hard

I would be genuinely interested (as much from an intellectual point of view than anything else) as to whether this is clearly still broken or flaky? I expect to spot one or two typos & errors in the "easy stuff" tomorrow, but I think the hard stuff is good...