C.W.K.
Stream
Lesson 02 of 06 · published

Crates, Packages & Dependencies

~11 min · tooling, crates, cargo, dependencies

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

A crate is Rust's unit of compilation; a package is what Cargo manages. Getting the vocabulary straight makes the whole dependency system click.

Crate vs package

A crate is a single compilation unit — either a binary (has a main) or a library (code other crates use). A package is one Cargo.toml and the crates it builds: a package can hold multiple binary crates and at most one library crate. When you cargo new, you get a package with one crate.

Dependencies from crates.io

You add a dependency by naming it under [dependencies] in Cargo.toml: serde = "1". Cargo fetches it from crates.io (the public registry), resolves compatible versions, and makes it available to use. cargo add serde does the edit for you. The whole ecosystem — over 150,000 crates — is one line away.

Cargo.toml declares; Cargo.lock pins. Cargo.toml states which versions you accept (serde = "1" means 'any 1.x'). Cargo.lock records the exact versions actually used, so builds are reproducible. Commit Cargo.lock for binaries — it's what makes 'works on my machine' actually transferable.

Semantic versioning

Crate versions follow semver: MAJOR.MINOR.PATCH. serde = "1" accepts any 1.x (backward-compatible) but not 2.0 (which may break). This is why editions and semver matter together — the ecosystem can evolve without silently breaking your build, because version bounds encode compatibility promises.

Code

Declaring dependencies in Cargo.toml·toml
# Cargo.toml
[package]
name = "my-app"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { version = "1", features = ["derive"] } # a library crate
rand = "0.8"

# Add via CLI instead of editing by hand:
#   cargo add serde --features derive
#   cargo build   # fetches deps, writes Cargo.lock

External links

Exercise

Create a binary package, add the rand crate with cargo add rand, and use it to print a random number. Inspect the generated Cargo.lock and find the exact version pinned. Why does committing that lockfile make your build reproducible for a teammate?
Hint
Cargo.toml might say rand = "0.8" (any 0.8.x), but Cargo.lock pins the exact patch version. Committing the lock means your teammate builds with the identical version you tested — eliminating 'works on my machine' from dependency drift.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.