Binary Crates
A binary crate compiles into a standalone executable program containing an entrypoint function fn main(). A single Cargo package can define one default binary (src/main.rs) as well as any number of additional binary targets inside src/bin/ or via explicit [[bin]] sections in Cargo.toml.
Multi-Binary Package Structure:
my_service/
├── Cargo.toml (name = "my_service", default-run = "my_service")
└── src/
├── lib.rs (Shared library code: `use my_service::...`)
├── main.rs --> Binary #1: `my_service`
└── bin/
├── migrate.rs --> Binary #2: `migrate` (single-file binary)
└── admin/
├── main.rs --> Binary #3: `admin` (multi-file binary)
└── commands.rs
Running and Building Specific Binary Targets
When a package defines more than one binary target, cargo run requires either the --bin <name> flag on the command line or the default-run key under [package] in Cargo.toml:
| Command | Behavior |
|---|---|
cargo run |
Runs the single binary target, or the binary specified by default-run in Cargo.toml. |
cargo run --bin migrate |
Compiles and runs the migrate binary (src/bin/migrate.rs). |
cargo run --bin migrate -- --dry-run |
Passes --dry-run as a command-line argument to migrate. |
cargo build --bins |
Compiles all binary targets in the package. |
cargo install --path . |
Compiles all binary targets in --release mode and copies them into ~/.cargo/bin/. |
Configuring Explicit [[bin]] Targets in Cargo.toml
By default, Cargo automatically discovers src/main.rs and src/bin/*.rs (autobins = true). Adding explicit [[bin]] array-of-tables entries in Cargo.toml lets you customize a binary's path, rename its output executable, or gate a binary behind optional Cargo features (required-features):
[package]
name = "cloud_toolkit"
version = "0.1.0"
edition = "2024"
default-run = "cloud-cli"
[[bin]]
name = "cloud-cli"
path = "src/main.rs"
[[bin]]
name = "cloud-tui"
path = "src/bin/tui.rs"
required-features = ["tui-dashboard"]
When required-features = ["tui-dashboard"] is set on cloud-tui, Cargo only builds cloud-tui when --features tui-dashboard (or --all-features) is enabled.
Sharing Code Across Multiple Binaries
Because two binary files (src/main.rs and src/bin/migrate.rs) cannot import modules directly from each other, place all shared types, configuration parsers, and database clients in src/lib.rs. Every binary in src/bin/ can then import the shared library crate cleanly:
// Shared module in `src/lib.rs` (crate `cloud_toolkit`):
pub mod shared_config {
#[derive(Debug)]
pub struct EnvConfig {
pub endpoint: &'static str,
pub timeout_secs: u64,
}
pub const DEFAULT_CONFIG: EnvConfig = EnvConfig {
endpoint: "https://api.example.com",
timeout_secs: 30,
};
}
// Binary entrypoint in `src/bin/migrate.rs`:
fn main() {
let cfg = shared_config::DEFAULT_CONFIG;
println!(
"Migration tool using endpoint {} (timeout={}s)",
cfg.endpoint, cfg.timeout_secs
);
}
Migration tool using endpoint https://api.example.com (timeout=30s)