Build-std and the standard library

Maybe the implicit opt-out could be simply triggered by mentioning dependencies.std at all, then using std = { optional = true } would be enough to remove it as a dependency? That would fit well with the large number of crates that have an existing std feature to activate some of their API, and for crates that never use std, having std available if someone activates the feature isn't a problem.

Though I'm not sure how this would interact with the implicit prelude, there might need to be some other flag to control the behaviour of that.

That wouldn't be backwards-compatible with existing cargo, though.

This sounds like a huge step backwards to me. Currently, I can trust the compiler to complain when I accidentally use e.g. HashMap in a no_std project. With runtime errors, I have to perform these checks manually. Not only for my code, but also for every pull request I review. This sounds very dangerous, especially for embedded code that must be highly reliable and should not panic.

3 Likes

A very simple way to do this could be to list all supported targets in a package.metadata.supported_targets configuration key in the Cargo.toml of libstd. Then cargo can check against this list and either build std or core/alloc.

Note the "some combination". My understanding of the portability lint proposals and what I thought this thread was proposing is that in no_std environments, types like HashMap would simply not be defined, and any code referencing would trivially not compile, the same way code using Windows-only APIs won't compile on Linux today. I assume runtime errors would be reserved for cases where conditional compilation is infeasible, like an OS API that either doesn't exist or radically changes behavior based on something we don't have a cfg for.

To me the thread description sounds more like cfg attributes are only planned if a runtime error is "not possible to express". Otherwise, runtime errors "like wasm" are proposed, which means runtime errors on e.g. thread::spawn.

That wouldn't work very well for custom target specs.

That means what's in the default set has to be set in stone when it is stabilized, as existing items could not be moved into a feature in the future. Although it is an important topic, I'd rather not diverge this thread into a discussion about features.

For now, that will not change. If we ever decide to build all of std for all targets (or you use -Zbuild-std), then you would get an unstable error (std would be unstable). Past that, I suspect there will be some solution if it is ever stabilized, but that is very far in the future.

Fair enough. It does affect the usefulness of merging std and core – and of leaving them separate – but only via a fairly long chain of indirections.

Regardless, I think I've changed my mind: I now think my proposal to use Cargo features favors merging std and core. As such, I withdraw my objections. That said, I agree with @phil_opp that runtime errors would be a big regression; I strongly prefer omitting unsupported APIs, and migrating to Cargo features in the future. But I realize that this doesn't need to be decided immediately, as long as hollow-std remains behind a feature flag.

For the record, this is why I now think we should merge them:

Before, I proposed that Cargo would interpret a Cargo.toml listing core as a dependency, but not listing std, as implying that the crate was no_std. But that's overcomplicated. There's a simpler approach:

  • The only standard library crate you could list as a Cargo.toml dependency would be std. core would be deprecated, as would #![no_std].
  • But there would be a Cargo feature / feature alias corresponding to the subset of std that's currently reexported from core.
  • Following the normal rules for Cargo features, if you didn't specify a list of features, you'd get the default feature set, which would be all of (current) std. If you specified features = ["core"], you'd get a std with only the functionality of core.

This would achieve a similar effect (i.e. mentioning core takes away the rest of std), but using Cargo's existing rules rather than adding a special case.

The remaining special case would be simply: "If you don't list a dependency on std, you get one by default." This special case could then potentially be removed in a future edition, although it might be better to just keep it permanently.

3 Likes

I basically agree with you: making configuring std for no-std behave very similarly to turning off the default features in another dependency seems like the best UX we could have. But I also hope we can avoid bogging down this implementation work in long-ranging interface discussions that don't need to block this work that would have to happen before we can possibly implement a new interface.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.