Skip to content

RFC: Extend manifest dependencies with used#3920

Open
epage wants to merge 10 commits intorust-lang:masterfrom
epage:used-deps
Open

RFC: Extend manifest dependencies with used#3920
epage wants to merge 10 commits intorust-lang:masterfrom
epage:used-deps

Conversation

@epage
Copy link
Contributor

@epage epage commented Feb 13, 2026

Extend Cargo's dependency specifications so users can mark that a dependency is used, independent of what unused externs says.

Important

When responding to RFCs, try to use inline review comments (it is possible to leave an inline review comment for the entire file at the top) instead of direct comments for normal comments and keep normal comments for procedural matters like starting FCPs.

This keeps the discussion more organized.

Rendered

@epage epage added the T-cargo Relevant to the Cargo team, which will review and decide on the RFC. label Feb 13, 2026

However, not all dependencies exist for using their `extern` in the current package, including
- activating a feature on a transitive dependency
- pinning the version of a transitive dependency in `Cargo.toml` (however, normally this would be done via `Cargo.lock`)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we could create a [peer-dependencies] section for that just like npm? This would also improve compile time by avoiding a need to block compilation of the current crate on said dependency. And cargo could get the option to warn if a peer-dependency is not used by any other crate (aka pinning a transitive dependency has no effect).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we could create a [peer-dependencies] section for that just like npm?

There is an ecosystem cost to adding new dependencies tables. We'd have to weigh the cost against the benefits.

This would also improve compile time by avoiding a need to block compilation of the current crate on said dependency.

For the two use cases we have, compilation will already be blocked on them. Do we know of any use cases where this would be a concern?

Copy link
Contributor Author

@epage epage Feb 13, 2026

Choose a reason for hiding this comment

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

Reading up on peerDependencies, they sounds like target."cfg(false)".dependencies which helps with version requirements but not with feature activation.

@joshtriplett
Copy link
Member

This looks great, and would enable us to warn about unused dependencies by default.

## Motivation
[motivation]: #motivation

Rusts can report to Cargo
Copy link
Member

Choose a reason for hiding this comment

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

I think you meant Rustc here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines +77 to +85
To resolve this, `user` can:
```toml
[package]
name = "user"

[dependencies]
abstraction = "1.0"
mechanism = { version = "1.0", features = ["semantic-addition"], used.reason = "feature activation" }
```
Copy link
Member

@ranger-ross ranger-ross Feb 17, 2026

Choose a reason for hiding this comment

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

One problematic scenario I see with this is that if later I remove abstraction without removing mechanism we no longer get the benefit of unused lint. In this case its fairly obvious, but for larger Cargo.toml files it might be easier to miss.

Though I think this is a solvable problem by extending the current design.
I am imagining a used.transitive which would could conditionally disable the unused lint if this dependency is also imported transitively. Now removing the abstraction dependency in the example would also lint that mechanism is unused.

Though I think this could be left as a future possibility :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

### `used`

Pros:
- Close to the dependency
Copy link
Member

Choose a reason for hiding this comment

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

Is that really such a benefit? When searching for how to modify the behavior of a lint, checking how is a lint being configured or why is a given dependency not being marked as unused, I wouldn't really think of going to the dependency table. Rather I would go look at the lint configuration. Similarly to when I configure e.g. disallowed functions (disallowed_methods) for Clippy, that's also not specified on the dependency.

The way I interpret the [dependencies] table is "how to fetch and build dependencies used by my crate", possibly extended by "how to expose my dependencies to external crates", once we get public/private dependencies.

But configuring lints seems like a separate concern, that is also much more of an implementation detail of the given crate, and it IMO makes more sense for it to live in lint config, rather than here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think there is a benefit. CPU caches are driven by the assumption of locality, that the same they will access memory that is close together over a close together period of time. I think locality of code is also an important consideration for us as humans to read and understand code and data. The perfect abstraction means you don't need to look at a function implementation, only its call site. Perfect abstractions don't exist but they are on a gradient. You can get away with a lower quality abstraction when the code is closer together while further away, you need a higher quality abstraction because it is harder to process and reason about because of the lack of locality.

I think the analogy with disallowed_methods doesn't work because you are applying a setting that affects a package globally while we are trying to control individual uses. We specifically call out, if we went with lint config, setting it in workspace.lints would be bad and that we likely would need a way to control which lints table the package exists in that we are referring to, to avoid being overly broad with this lint control.

That said, this isn't the only consideration here. We are weighing multiple benefits and downsides.

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.

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants