Cargo and Package Management

Optional Dependencies

An optional dependency (optional = true in [dependencies]) is an external crate that is only downloaded, compiled, and linked when a corresponding Cargo feature is enabled. Using optional dependencies keeps default compile times fast for users who do not need optional integrations such as serde serialization, database drivers, or compression codecs.

[dependencies]
serde = { version = "1.0", optional = true }
lz4_flex = { version = "0.11", optional = true }

[features]
serde = ["dep:serde"]           <-- Explicit `dep:` syntax (Rust 1.60+ / 2024)
compression = ["dep:lz4_flex"]

Explicit dep:<crate> Syntax vs Implicit Feature Names

When you mark a dependency optional = true:

  1. Implicit behavior: If you do not mention dep:<crate> anywhere inside [features], Cargo implicitly creates a feature with the exact same name as the dependency (serde).
  2. Explicit dep:<crate> syntax (Recommended): Writing serde = ["dep:serde"] or compression = ["dep:lz4_flex"] inside [features] explicitly binds the feature name to the optional dependency and suppresses the implicit lz4_flex feature name, preventing internal crate names from leaking as public feature flags!

Enabling Downstream Dependency Features (crate?/feature)

Often, enabling a feature on your crate should also enable a corresponding feature on an optional or required dependency:

  • "serde/derive": Enables the derive feature on serde and activates serde if it is optional.
  • "serde?/std" (Weak dependency feature): Enables the std feature on serde only if serde is already enabled elsewhere, without forcing serde to be compiled on its own!
[package]
name = "event_bus"
version = "0.2.0"
edition = "2024"

[dependencies]
serde = { version = "1.0", default-features = false, optional = true }
tracing = { version = "0.1", default-features = false, optional = true }

[features]
default = ["std"]
# Weak `?/std` syntax: if `serde` or `tracing` is enabled, also enable their `std` feature,
# without pulling in `serde` or `tracing` when only `std` is enabled!
std = ["serde?/std", "tracing?/std"]
serde = ["dep:serde", "serde/derive"]
observability = ["dep:tracing"]

Conditionally Implementing External Traits

In Rust source code, guard optional dependency imports and impl blocks with the feature flag:

#[derive(Debug, PartialEq, Eq)]
pub struct EventId(pub u64);

// When `--features serde` is enabled, this conditional module or impl is compiled:
#[cfg(feature = "serde")]
mod serde_support {
    // use serde::{Serialize, Deserialize};
}

impl EventId {
    pub fn to_wire_string(&self) -> String {
        format!("evt_{:016x}", self.0)
    }
}

fn main() {
    let evt = EventId(255);
    println!("Wire representation: {}", evt.to_wire_string());
}
Wire representation: evt_00000000000000ff
Syntax in [features] Effect
foo = ["dep:bar"] Enables optional dependency bar when feature foo is active, without exposing bar as its own feature flag.
foo = ["bar/derive"] Enables optional dependency bar and turns on bar's derive feature.
std = ["bar?/std"] Turns on bar's std feature only if bar is already enabled by another feature.