How to enable Cargo features only for dev dependencies

A recipe on how to enable Cargo features only for dev dependencies.

I was trying to figure out how to enable features only for dev dependencies in Cargo. I want tokio to have the macros feature when running tests, but not during normal builds.

It turns out you can do this with resolver version 2. You just specify the same dependency in both [dependencies] and [dev-dependencies] with different feature sets.

First, make sure you’re using resolver version 2. Set it in your [package] or [workspace] section:

TOML
[package]
name = "my-package"
version = "0.1.0"
edition = "2024"
resolver = "2"

Edition 2024 defaults to resolver version 2, and 2021 does too. So you might already have it enabled.

Here’s the pattern. Specify the dependency without dev-specific features in [dependencies] and with dev-specific features in [dev-dependencies]:

TOML
[dependencies]
tokio = { version = "1", features = ["rt", "sync", "io-std"] }

[dev-dependencies]
tokio = { version = "1", features = ["rt", "sync", "macros", "io-std"] }

With resolver version 2, features enabled on dev-dependencies stay isolated unless you’re building dev targets. So:

The macros feature is only enabled when building tests, examples, or benchmarks. This is exactly what I wanted.

Before resolver version 2, Cargo would unify features across all uses of a dependency. If you enabled macros in dev-dependencies, it would also be enabled for normal dependencies. That’s not what I want.

For workspaces, set the resolver at the workspace level:

TOML
[workspace]
members = ["crates/*"]
resolver = "2"

[workspace.dependencies]
tokio = { version = "1" }

[dependencies]
tokio = { workspace = true, features = ["rt", "sync", "io-std"] }

[dev-dependencies]
tokio = { workspace = true, features = ["rt", "sync", "macros", "io-std"] }

This keeps things consistent across your workspace while still allowing different feature sets for different build contexts.