Skip to content

Conversation

@davidtwco
Copy link
Member

@davidtwco davidtwco commented Oct 31, 2025

Allow users to add explicit dependencies on standard library crates in the Cargo.toml. This enables Cargo to determine which standard library crates are required by the crate graph without build-std-crates being set and for different crates to require different standard library crates.

This RFC is is part of the build-std project goal and a series of build-std RFCs:

  1. build-std context (build-std: context #3873)
  2. build-std="always" (build-std: always #3874)
  3. Explicit standard library dependencies (this RFC)
  4. build-std="compatible" (RFC not opened yet)
  5. build-std="match-profile" (RFC not opened yet)

There is also a Zulip channel where you can ask questions about any of the build-std RFCs. This series of RFCs was drafted over many months with the help of stakeholders from many Rust project teams, we thank them for their help!

There are some details that have been noted as being worth mentioning in any eventual tracking issues:

Rendered

@davidtwco davidtwco added T-cargo Relevant to the Cargo team, which will review and decide on the RFC. T-crates-io Relevant to the crates.io team, which will review and decide on the RFC. labels Oct 31, 2025
@Turbo87 Turbo87 moved this to For next meeting in crates.io team meetings Oct 31, 2025
Comment on lines +328 to +338
..is equivalent to the following explicit dependency on `std`:

```toml
[package]
name = "hello_world"
version = "0.1.0"
edition = "2024"

[dependencies]
std = { builtin = true, public = true }
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I think this is a sensible default, I think there should be a warn-by-default lint if this ever occurs for a crate annotated with #![no_std], since we should be pushing people toward adding an alloc or core dependency instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, a lint in the compiler if we end up passing --extern std=... to rustc but with a #![no_std] crate? That makes sense to me but the part of the document you've commented on is about public/private dependencies which seems unrelated, so wanted to double check.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, if:

  1. Cargo.toml doesn't have any std/alloc/core dependencies
  2. The crate is no_std

It may require explicit support from Cargo to do this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this to the future possibility related to #![no_std] in 3935863, depending on whether migrating away from #![no_std] entirely is feasible, this could be an alternative.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's reasonable; I'll resolve this for now. I definitely think it's okay to punt this off until later.

Comment on lines +122 to +127
If there is an optional dependency on the standard library then Cargo will
validate that there is at least one non-optional dependency on the standard
library (e.g. an optional `std` and non-optional `core` or `alloc`, or an
optional `alloc` and non-optional `core`). `core` cannot be optional. For
example, the following example will error as it could result in a build without
`core` (if the `std` feature were disabled):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it is an unstable feature, I think it's worth commenting on how this interacts with the no_core feature: will crates using this still have to specify the core dependency, or will it be allowed to be omitted in those cases? Additionally, would this require an additional cargo feature or would just the feature on the crate be enough?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't considered no_core because it's perma-unstable. I don't see us ever stabilising it unless we're comfortable with alternative core crates having no stability guarantees (otherwise we can't add new required language items, which is very useful). Given that there is no path to or interest in stabilising this feature, I don't think it is worth mentioning in the RFC - a proposal to stabilise no_core can mention interactions with build-std rather than this RFC anticipating it.

Copy link
Contributor

@Ericson2314 Ericson2314 Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly consider how annoying opt-out traits have been on the language design level, and "default-features" and feature migrations too, so we don't repeat the same mistakes here.

If we require core-only users to explicitly depend on core, yes they have type a few more characters, but we side step all those problems from the get-go.

(For what is is worth, the outcome I think is most likely is that in the coming decade, as we find ways to move more standard library functionality onto crates.io "full userland", core eventually becomes something that also depends on other crates. This is exactly analogous to the "oops, Sized is now longer the first trait in the hierarchy" problem, and close to the "oops, I want to add a new default feature, how do people opt out?" problem.)

Writing the core dependency one per crate is a lot cheaper than writing T: Sized in every bound, so let's just do it! Problem avoided!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-cargo users are important to consider here - they may need to add features to their own build systems if passing in stdlib crates via --extern becomes mandatory in any sense.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we require core-only users to explicitly depend on core, yes they have type a few more characters, but we side step all those problems from the get-go.

This is exactly what we already propose, if you depend only on core then you need to explicitly write your dependency on core to remove the implicit dependencies on alloc and std.

Copy link
Contributor

@Ericson2314 Ericson2314 Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK that sounds fine for external creates. Sorry for my misunderstanding.

New related question, that I think needs a call-out in the RFC:

How is core itself to be packaged? (Or really all standard library crates?) It would be nice for them to explicitly have no stdlib deps, rather than rely on today's hacks.

I see two approaches, which I'll Alan-Kay-style call eager and late binding:

  • late binding: Standard library crates use builtin = true on other standard library crates, and rely on the perma-unstable patching mechanism to make those refer to the right crates (that are presumably in the repo).

  • eager binding: standard library creates just refer to each other with path dependencies, like today.

I think late binding is better. But the problem exist either way:

  • In the case of late binding, core needs a way to have no (implicit or explicit) standard library deps, so it doesn't get a dependency on itself in the build-std = "always" case.

  • In the case of early binding, all standard library crates need to have no (implicit or explicit) standard library deps for the same reason, because their path deps on their fellow standard library crates serve that purpose instead.


[dependencies]
std = { builtin = true, optional = true }
# error: must have a non-optional dependency on core
Copy link

@clarfonthey clarfonthey Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it might be worth mentioning the concept of lints here to make this less annoying of a requirement.

Right now, we have the concept of cargo lints, but we don't have any lints that let you autofix Cargo.toml, which I think would be a much-appreciated addition here. I feel like it makes a substantial difference toward this being a reasonable requirement if, instead of a hard error, this were a warn-by-default lint (implicitly adding the core dependency anyway) and that crates like this simply could not be published to crates.io.

In particular, it would be quite annoying if this issue completely prevented rust-analyzer from operating on code until it were fixed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced this is that annoying a requirement. Users will only hit this after adding an explicit optional dependency on the alloc or std and immediately notice the error and need to fix it. I don't see the value in having a lint that would allow you to delay fixing this but would force you to fix it eventually when you want to publish the crate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that my main opinion here is that it's something that requires immediate attention to be fixed when it doesn't really affect the ability to build the code. If someone runs cargo add std --optional, this suddenly prevents rust-analyzer from working at all until it's fixed, and while the error should definitely be fixed before publishing the package, I fail to see how this should completely deadlock cargo's ability to build the crate at all until it's fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo add could just automatically add core if you're adding std or alloc.

@davidtwco davidtwco force-pushed the build-std-part-three-explicit-dependencies branch from eafc6b2 to 4b870f6 Compare November 3, 2025 15:09
@ehuss ehuss moved this to RFC needs review in Cargo status tracker Nov 4, 2025
> ├── rand v0.7.3
> │ ├── getrandom v0.1.14
> │ │ ├── cfg-if v0.1.10
> │ │ │ └── core v0.0.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get why these don't have a usable version number, but IMHO, this should either display as core (built-in) or use the current Rust toolchain version instead. I can see the latter being considered very useful for the theoretical long-term case where crates could potentially be compiled with different libstd versions and then linked together, e.g. via dynamic linking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-cargo Relevant to the Cargo team, which will review and decide on the RFC. T-crates-io Relevant to the crates.io team, which will review and decide on the RFC.

Projects

Status: RFC needs review
Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants