Files
Leonardo Maldonado 5f0b2079f8 feat: expand to 218 rules and refresh for the Rust 2024 edition
Add unsafe, concurrency, conversions, and pattern-matching categories plus rules across existing ones, and correct advice that was outdated for Rust 1.96.
2026-06-14 19:38:44 -03:00

2.2 KiB

proj-feature-additive

Design Cargo features to be strictly additive

Why It Matters

Cargo unifies features across the dependency graph: if any crate in the build enables a feature, every consumer of that crate gets it. A feature that removes or changes existing behavior will break crates that depend on the baseline behavior the moment a third dependency enables it. Features must only add capability — new trait impls, additional dependencies, optional integrations — never subtract. Mutually exclusive features are an anti-pattern in the Cargo model.

Bad

[features]
# "no_std" disables std — enabling it REMOVES behavior
no_std = []

[dependencies]
# and somewhere in lib.rs:
# #[cfg(not(feature = "no_std"))]
# use std::collections::HashMap;
// lib.rs — toggling off std via a feature is non-additive
#[cfg(not(feature = "no_std"))]
use std::vec::Vec;

#[cfg(feature = "no_std")]
use alloc::vec::Vec;

Good

[features]
# "std" ADDS std support; no_std is the baseline
default = ["std"]
std = []

# Optional integrations — purely additive
serde = ["dep:serde"]
tokio = ["dep:tokio"]

[dependencies]
serde = { version = "1", optional = true }
tokio = { version = "1", optional = true }
// lib.rs — std is opt-in, no_std is the default baseline
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
use std::vec::Vec;

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

Rules for Additive Features

  • A feature may add new items, trait impls, or dependencies — never gate-away existing ones.
  • If you ship a no_std crate, make std a feature in default, not the other way around.
  • Mutually exclusive features (e.g. backend-a vs backend-b) cannot be enforced by Cargo; emit a compile-time error via compile_error! if both are set, and document the limitation clearly.
  • Use dep: syntax (dep:serde) to keep optional dependency names out of the feature namespace.

See Also