Feature flags are additive switches, and default = ["json"] means "json is on unless the consumer says otherwise". A consumer that writes default-features = false to trim its build silently turns every default feature off, including the one it needed. The crate still compiles and runs — it just takes the other branch of every #[cfg(feature = ...)], and the output changes with no error.
pub fn export(value: i32) -> String {
#[cfg(feature = "json")]
{
format!("{{\"value\": {}}}", value)
}
#[cfg(not(feature = "json"))]
{
format!("value={}", value)
}
}# exporter's Cargo.toml has: default = ["json"]
[dependencies]
exporter = { path = "../exporter", default-features = false }
# cargo run -> value=42 (expected {"value": 42})crates.io is Rust's central package registry; cargo publish uploads a crate there after packaging it according to Cargo.toml's metadata (name, version, and description are required, and so is a license — as either a standard SPDX identifier in the license field, or, for something crates.io's checker doesn't recognize as a standard license, a license-file pointing at the actual license text; you don't need both). Feature flags, declared in a [features] section, let a crate make parts of its functionality optional, reducing compile time and dependency footprint for consumers who don't need everything. A Cargo workspace groups multiple crates that share a single Cargo.lock and target directory, common for a project split into a library plus one or more binaries.
Publishing a crate
Required metadata lives in Cargo.toml; publishing requires a crates.io account linked via an API token, and once a version is live it's permanent — cargo yank marks a version as "don't use this for new projects" without deleting it, since other published crates may already depend on the exact version.
[package]
name = "my_crate"
version = "0.1.0"
edition = "2024"
license = "MIT"
description = "A short description of what this crate does"$ cargo login # one-time: link your crates.io API token
$ cargo publish # packages and uploads the crateFeature flags: optional functionality
Consumers opt into features explicitly in their own Cargo.toml, keeping unused code (and its dependencies) out of builds that don't need it.
[features]
default = ["json"]
json = ["dep:serde_json"]
yaml = ["dep:serde_yaml"]
[dependencies]
serde_json = { version = "1.0", optional = true }
serde_yaml = { version = "0.9", optional = true }Opting out of the defaults means opting back in to whichever ones you actually use. Naming the feature explicitly keeps the slimmer build for everything else while restoring the behaviour the app depends on:
[dependencies]
exporter = { path = "../exporter", default-features = false, features = ["json"] }
# cargo run -> {"value": 42}Workspaces: multiple crates, one build
A workspace's top-level Cargo.toml lists its member crates, which then share a single Cargo.lock (guaranteeing every member uses the same dependency versions) and a single target directory (avoiding redundant recompilation across crates).
[workspace]
members = ["app", "core", "cli"]