Cargo.toml is the manifest file that defines everything about your Rust project. It uses TOML Format:
authors = [ " Your Name <you@example.com> " ]
description = " A short description "
repository = " https://github.com/user/my_crate "
keywords = [ " example " , " tool " ]
categories = [ " command-line-utilities " ]
serde = { version = " 1 " , features = [ " derive " ] }
tokio = { version = " 1 " , features = [ " full " ] }
clap = { version = " 4 " , features = [ " derive " ] }
Field Purpose nameCrate name (must match ^[a-zA-Z0-9_-]+$) versionSemVer version (e.g., 0.1.0``1.2.3-beta.1) editionRust edition (2015``2018``2021``2024) rust-versionMinimum supported Rust version (MSRV) licenseSPDX license identifier (MIT``Apache-2.0``GPL-3.0) repositorySource code URL readmePath to README file categoriescrates.io categories (max 5)
Workspaces allow you to manage multiple related crates in a single repository:
├── Cargo.toml # workspace root
repository = " https://github.com/user/my-project "
serde = { version = " 1 " , features = [ " derive " ] }
tokio = { version = " 1 " , features = [ " full " ] }
Each member crate inherits from the workspace:
## crates/core/Cargo.toml
thiserror.workspace = true
cargo build # build all workspace members
cargo build -p my-core # build specific member
cargo test --workspace # test all members
cargo test -p my-core # test specific member
cargo run -p my-cli # run specific binary
cargo tree -p my-server # dependency tree for member
cargo metadata --format-version 1 # machine-readable workspace metadata
The same versions of shared dependencies, avoiding the diamond dependency problem.Features are optional dependencies or conditional compilation flags:
full = [ " serde " , " tokio " , " tracing " ]
serde = [ " dep:serde " , " dep:serde_json " ]
tracing = [ " dep:tracing " , " dep:tracing-subscriber " ]
serde = { version = " 1 " , optional = true }
serde_json = { version = " 1 " , optional = true }
tokio = { version = " 1 " , optional = true , features = [ " full " ] }
tracing = { version = " 0.1 " , optional = true }
tracing-subscriber = { version = " 0.3 " , optional = true }
#[cfg(feature = "serde" )]
use serde :: { Serialize , Deserialize };
#[cfg(feature = "serde" )]
#[derive( Serialize , Deserialize )]
#[cfg(not(feature = "serde" ))]
When two crates in the same dependency graph enable different features of a shared dependency, Cargo Unifies them — all enabled features are active for all dependents. This can cause unexpected Behavior:
shared = { version = " 1 " , features = [ " feature-x " ] }
shared = { version = " 1 " , features = [ " feature-y " ] }
In a workspace depending on both crate-a and crate-b``shared will have both feature-x and feature-y enabled. If feature-y has heavy dependencies, crate-a users pay the cost even though They only requested feature-x.
Keep features additive. Never use features to remove functionality. Use default = [] for library crates. Let users opt in to features. Use dep:serde syntax to gate the dependency itself, not just code. Document all features in the crate”s README. Profiles control compiler optimization settings:
opt-level = 0 # no optimization
debug = true # full debug info
debug-assertions = true # enable debug assertions
overflow-checks = true # integer overflow panics
lto = false # no link-time optimization
codegen-units = 256 # fast compile, no cross-crate optimization
incremental = true # incremental compilation
opt-level = 3 # maximum optimization
debug = false # no debug info
overflow-checks = false # wrapping arithmetic (no panics)
lto = true # link-time optimization
codegen-units = 1 # single codegen unit (better optimization, slower compile)
strip = true # strip symbols from binary
panic = " abort " # smaller binary (no unwinding)
cargo build --profile profiling
LTO performs optimizations across crate boundaries. It increases compile time significantly but can Reduce binary size by 10-20% and improve runtime performance by 5-10%.
lto = false: No LTO (default for dev)lto = "thin": Thin LTO — faster than full LTO, most of the benefitlto = true (or lto = "fat"): Full LTO — best optimization, slowest compileFor release builds in production, lto = "thin" is a good default. Use lto = true for maximum Performance-critical builds.
Cargo uses Semantic Versioning. A version requirement like "1.2" is equivalent to ">= 1.2.0, < 2.0.0":
serde = " 1 " # >= 1.0.0, < 2.0.0
tokio = " 1.35 " # >= 1.35.0, < 2.0.0
clap = " ^4.4 " # same as "4.4" (caret is default)
exact = " =1.0.0 " # exactly 1.0.0
range = " >=1.0, <2.0 " # explicit range
serde = { version = " 1 " , features = [ " derive " ] }
my-lib = { git = " https://github.com/user/my-lib " , branch = " main " }
local-lib = { path = " ../local-lib " }
tokio = { version = " 1 " , default-features = false , features = [ " rt-multi-thread " , " macros " ] }
# Optional dependency (only compiled when the feature is enabled)
optional-dep = { version = " 1 " , optional = true }
Cargo.lock pins exact dependency versions. It should be committed to version control for:
Binary crates (applications, CLIs) Libraries where exact reproducibility matters It should NOT be committed for library crates that are published to crates.io (the library’s Dependents should resolve their own compatible versions).
cargo update # update all dependencies within SemVer bounds
cargo update -p serde # update only serde
cargo update --precise 1.0.0 serde # pin serde to exact version
cargo build # debug build
cargo build --release # release build (optimized)
cargo build --target x86_64-unknown-linux-musl # cross-compile
cargo build -j 8 # parallel jobs (default: num_cpus)
cargo check # type-check without producing binary (fast)
cargo clean # remove target/
cargo test # run all tests
cargo test --release # run tests with release optimizations
cargo test -- --test-threads=1 # run tests sequentially (for debugging)
cargo test -- --nocapture # show println! output
cargo test my_module # run tests in specific module
cargo test -- test_name # run specific test by name
cargo doc # generate documentation
cargo doc --open # generate and open in browser
cargo doc --no-deps # only document this crate (not dependencies)
cargo doc --document-private-items # include private items
cargo clippy # run clippy lints
cargo clippy -- -W clippy::all -D warnings # treat warnings as errors
cargo fmt -- --check # check formatting without modifying files
cargo audit # check for known security vulnerabilities
cargo outdated # check for outdated dependencies
cargo tree # display dependency tree
cargo tree --duplicates # show duplicate dependencies
cargo udeps # find unused dependencies
cargo machete # determine which dependencies are unused
cargo login # authenticate with crates.io
cargo publish # publish to crates.io
cargo publish --dry-run # verify without publishing
cargo publish --allow-dirty # publish with uncommitted changes (not recommended)
Unit tests live in the same file as the code they test, inside a #[cfg(test)] module:
fn add (a : i32 , b : i32 ) -> i32 {
assert_eq! ( add ( 2 , 3 ), 5 );
assert_eq! ( add ( - 1 , 1 ), 0 );
#[should_panic(expected = "overflow" )]
// cargo test -- --ignored
Integration tests live in the tests/ directory and test the crate as an external consumer would:
│ │ └── mod.rs # shared test utilities
│ ├── integration_test.rs
fn test_add_from_external () {
assert_eq! ( add ( 10 , 20 ), 30 );
Each file in tests/ is compiled as a separate crate, so they cannot access src/ internals (only The public API). The tests/common/mod.rs pattern allows sharing test utilities.
Documentation tests are Rust code blocks in doc comments:
/// Adds two numbers together.
/// assert_eq!(add(2, 3), 5);
pub fn add (a : i32 , b : i32 ) -> i32 {
Doc tests are run by cargo test and serve as both documentation and tests. They verify that Examples in documentation actually compile and produce correct results.
use proptest :: prelude ::* ;
fn add_is_commutative (a in - 1000 i32 .. 1000 , b in - 1000 i32 .. 1000 ) {
assert_eq! ( my_crate :: add (a, b), my_crate :: add (b, a));
fn add_associative (a in - 1000 i32 .. 1000 , b in - 1000 i32 .. 1000 , c in - 1000 i32 .. 1000 ) {
let left = my_crate :: add ( my_crate :: add (a, b), c);
let right = my_crate :: add (a, my_crate :: add (b, c));
fn vec_sort_is_sorted (input in proptest :: collection :: vec ( proptest :: arbitrary :: any :: < i32 >(), 0 .. 100 )) {
let mut sorted = input . clone ();
for window in sorted . windows ( 2 ) {
prop_assert! (window[ 0 ] <= window[ 1 ]);
Proptest generates random inputs, finds minimal failing cases (shrinking), and can run thousands of Test cases per second. It is particularly effective for finding edge cases that hand-written tests Miss.
criterion = { version = " 0.5 " , features = [ " html_reports " ] }
use criterion :: {black_box, criterion_group, criterion_main, Criterion };
fn bench_add (c : &mut Criterion ) {
c . bench_function ( "add" , | b | {
b . iter ( || black_box ( my_crate :: add ( black_box ( 2 ), black_box ( 3 ))))
criterion_group! (benches, bench_add);
criterion_main! (benches);
Criterion provides statistical analysis (mean, median, standard deviation), regression detection, And HTML reports with plots. It is the standard benchmarking tool in the Rust ecosystem.
/// A 2D point in Euclidean space.
/// This struct represents a point with x and y coordinates.
/// let p = Point::new(1.0, 2.0);
/// assert_eq!(p.x(), 1.0);
/// This struct does not panic on construction.
/// This struct does not return errors.
/// This struct is safe to use from any thread.
/// * `x` - The x coordinate
/// * `y` - The y coordinate
pub fn new (x : f64 , y : f64 ) -> Self {
/// Returns the x coordinate.
/// This item is documented.
#[doc(alias = "Point2D" )]
#[doc(html_root_url = "https://docs.rs/my-crate/" )]
pub struct Point { /* ... */ }
/// This module contains internal utilities.
#[doc(hidden)] // hidden from documentation
pub mod internal { /* ... */ }
perf record -g target/release/my_binary
# Record with call graphs
perf record --call-graph dwarf target/release/my_binary
flamegraph = " 0.6 " # install with: cargo install flamegraph
cargo flamegraph --bin my-binary
# Generates flamegraph.svg
For async applications, tokio-console provides real-time task inspection:
tokio = { version = " 1 " , features = [ " tracing " ] }
console-subscriber = " 0.4 "
RUSTFLAGS = " --cfg tokio_unstable " cargo run
tracing-subscriber = { version = " 0.3 " , features = [ " env-filter " , " json " ] }
use tracing :: {info, warn, error, instrument};
use tracing_subscriber :: EnvFilter ;
tracing_subscriber :: fmt ()
EnvFilter :: try_from_default_env ()
. unwrap_or_else ( | _ | EnvFilter :: new ( "info" ))
. json () // structured JSON logging
info! ( "application started" );
async fn process_request (id : u64 ) {
info! ( "processing request" );
// Automatically logs function entry/exit with timing
The #[instrument] attribute automatically creates a span that logs function entry, exit, and Elapsed time. It captures all function arguments by default (use skip and fields to control what Is captured).
serde = { version = " 1 " , features = [ " derive " ] }
use serde :: { Serialize , Deserialize };
#[derive( Serialize , Deserialize , Debug )]
#[serde(default = "default_workers" )]
fn default_workers () -> usize { 4 }
let json = serde_json :: to_string ( & config) . unwrap ();
let parsed : Config = serde_json :: from_str ( & json) . unwrap ();
Serde is the de facto serialization framework. It supports JSON, YAML, TOML, MessagePack, CBOR, BSON, XML, and custom formats. The #[serde] attribute provides fine-grained control over field Names, defaults, serialization behavior, and conditional compilation.
tokio = { version = " 1 " , features = [ " rt-multi-thread " , " macros " , " net " , " io-util " , " fs " , " time " , " sync " ] }
Key features to enable:
rt-multi-thread: Multi-threaded schedulermacros: #[tokio::main] and #[tokio::test]net: TCP/UDP networkingio-util: Async I/O utilitiesfs: Async file system operationstime: Timers and delayssync: Async mutex, channels, watch, notifyclap = { version = " 4 " , features = [ " derive " ] }
#[command(name = "my-tool" )]
#[command(about = "A useful tool" , long_about = None )]
#[arg(short, long, default_value_t = false)]
/// Number of threads (default: number of CPUs)
#[arg(short = 'j' , long, default_value_t = num_cpus :: get())]
let args = Args :: parse ();
println! ( "input: {}, output: {:?}" , args . input, args . output);
let sum : i64 = ( 1 ..= 1_000_000 ) . par_iter () . sum ();
// Parallel map + collect
let results : Vec < i32 > = data . par_iter ()
. map ( | x | expensive_transform ( * x))
let mut data = vec! [ 3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ];
Rayon converts sequential iterators to parallel iterators by changing .iter() to .par_iter(). The work-stealing scheduler automatically balances load across threads.
use itertools :: Itertools ;
let data = vec! [ 1 , 2 , 3 , 4 , 5 ];
for chunk in & data . into_iter () . chunks ( 2 ) {
let chunk : Vec <_> = chunk . collect ();
println! ( "{:?}" , chunk); // [1, 2], [3, 4], [5]
for combo in ( 1 ..= 4 ) . combinations ( 2 ) {
let joined = vec! [ "a" , "b" , "c" ] . into_iter () . intersperse ( ", " ) . collect :: < String >();
let groups = vec! [ 1 , 1 , 2 , 3 , 3 , 3 ] . into_iter () . group_by ( |& k | k);
tracing-subscriber = " 0.3 "
use tracing :: {info, warn, error, span, Level };
use tracing_subscriber :: fmt;
let span = span! ( Level :: INFO , "request" , id = 42 );
let _guard = span . enter ();
info! ( "processing started" );
warn! ( "rate limit approaching" );
error! ( "database connection failed" );
tokio = { version = " 1 " , features = [ " full " ] }
serde = { version = " 1 " , features = [ " derive " ] }
use axum :: { Router , routing :: get, Json };
async fn hello () -> Json < Hello > {
Json ( Hello { message : "world" . into () })
let app = Router :: new () . route ( "/" , get (hello));
let listener = tokio :: net :: TcpListener :: bind ( "0.0.0.0:3000" ) .await. unwrap ();
axum :: serve (listener, app) .await. unwrap ();
Before adding a dependency, evaluate it:
Criterion How to Check Downloads crates.io page — monthly downloads Last update crates.io — last publish date Maintenance GitHub — open issues, PRs, commit frequency Dependencies cargo tree -p crate-name — dependency countBinary size impact cargo bloat --release — size contributionBuild time cargo build --timings — incremental and clean build timesLicense compatibility crates.io — license field MSRV README or Cargo.toml rust-version field Audit cargo audit — known CVEs
Chain attacks, and licensing issues. Minimize your dependency tree. Audit regularly with `cargo audit`. For security-critical projects, consider `cargo-vet` (supply chain verification).Not committing Cargo.lock for binaries. Without Cargo.lockDifferent builds may resolve different dependency versions, leading to non-reproducible builds. Always commit Cargo.lock for applications and CLIs.
Feature flags causing unexpected compilation. A dependency might enable a feature that pulls in heavy transitive dependencies. Use cargo tree --features feature-name to inspect what features enable. Use default-features = false and select only the features you need.
Ignoring clippy warnings. Clippy catches real bugs (unused results, redundant clones, incorrect mutex usage). Set up CI to fail on clippy warnings: cargo clippy -- -D warnings.
Not writing integration tests. Unit tests verify internal logic but do not test the public API. Integration tests exercise the crate as a consumer would, catching issues with module visibility, feature gating, and API ergonomics.
Unoptimized release builds. The default Cargo.toml has minimal release profile settings. Add lto = "thin" and codegen-units = 1 for significant performance improvements in production.
Dependency bloat. Adding tokio with features = ["full"] enables everything including the full I/O driver, process spawning, signal handling, and more. Only enable the features you actually use: features = ["rt-multi-thread", "macros", "net"].
Not using workspace inheritance. Without workspace-level dependency management, different crates in the workspace may use different versions of the same dependency, increasing compile time and binary size.
Ignoring MSRV (Minimum Supported Rust Version). If your crate is published, specify rust-version in Cargo.toml. Users on older Rust compilers will get a clear error instead of cryptic compilation failures.
Running benchmarks without --release. Benchmarks compiled in debug mode are meaningless. The optimizer has not run, and timings reflect debug assertion overhead, not actual performance. Always run cargo bench (which uses the bench profile with opt-level = 3).
Not auditing dependencies. Run cargo audit regularly to check for known security vulnerabilities. Use cargo-deny to enforce license and advisory policies in CI. Consider cargo-vet for supply chain integrity in critical projects.
This topic covers the biological principles of cargo and ecosystem, including key concepts, experimental evidence, and real-world applications.
Key concepts include:
key biological principles and concepts experimental methods and data analysis applications of biology in medicine and industry ethical considerations in biological research the relationship between structure and function Success requires the ability to recall specific factual content, apply knowledge to novel scenarios, and evaluate experimental evidence critically.
Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.
## Intuition
Cargo is Rust’s build system and package manager, handling compilation, dependency resolution, and publishing. Crates.io hosts the ecosystem with semantic versioning. Workspaces enable monorepo structures. Cargo features allow conditional compilation, and build scripts handle code generation. The ecosystem’s emphasis on small, composable crates means most functionality comes from libraries rather than the standard library, making dependency management a core skill.
[[rust/05-traits-generics/traits-and-generics]] - Trait conventions in the ecosystem [[rust/04-error-handling/error-handling]] - Error handling crate conventions [[rust/06-concurrency/concurrency]] - Async runtime ecosystem [[rust/03-structs-enums/structs-and-enums]] - Derive macros for common traits