Packages & Crates
A crate is the smallest amount of code that the Rust compiler considers at a time. A package is one or more crates that provide a set of functionality. Understanding packages and crates is essential for organizing your Rust code and managing dependencies with Cargo.
What is a Crate?
A crate is a compilation unit in Rust. There are two types of crates:
1. Binary Crate
A binary crate is a program you can compile to an executable that you can run.
Every package has at most one binary crate, and it must be in
src/main.rs.
// src/main.rs - Binary crate
fn main() {
println!("Hello, world!");
}
2. Library Crate
A library crate doesn't have a main() function and doesn't compile
to an executable. Instead, it defines functionality intended to be shared with
multiple projects. Library crates are in src/lib.rs.
// src/lib.rs - Library crate
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
src/main.rs) and a library crate (in src/lib.rs). The
binary crate can use the library crate as if it were an external crate.
What is a Package?
A package is one or more crates that provide a set of functionality.
A package contains a Cargo.toml file that describes how to build
those crates.
Rules for packages:
- A package must contain at most one library crate
- A package can contain as many binary crates as you want
- A package must contain at least one crate (either library or binary)
Cargo.toml File
The Cargo.toml file defines your package:
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = "1.0"
serde_json = "1.0"
edition field specifies which Rust
edition your package uses. Current editions are "2015", "2018", and "2021". Use
"2021" for new projects.
Click Run to execute your code
Managing Dependencies
Dependencies are external crates your package needs. They're specified in
Cargo.toml:
Adding Dependencies
Add dependencies to the [dependencies] section:
[dependencies]
serde = "1.0"
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
Then run cargo build to download and compile dependencies.
Version Specifications
You can specify versions in different ways:
| Specification | Meaning | Example |
|---|---|---|
"1.0" |
Compatible with 1.0.x | serde = "1.0" |
"^1.0" |
Compatible with 1.x.x (default) | serde = "^1.0" |
"~1.0" |
Compatible with 1.0.x only | serde = "~1.0" |
"=1.0.0" |
Exact version | serde = "=1.0.0" |
"*" |
Any version (not recommended) | serde = "*" |
Dependency Sources
Dependencies can come from different sources:
1. From crates.io (Default)
[dependencies]
serde = "1.0" # From crates.io
2. From Git Repository
[dependencies]
my_crate = { git = "https://github.com/user/repo", branch = "main" }
3. From Local Path
[dependencies]
my_crate = { path = "../my_crate" }
4. From Workspace
[dependencies]
my_crate = { workspace = true }
Click Run to execute your code
Features
Many crates support optional features that you can enable:
[dependencies]
tokio = { version = "1.0", features = ["full", "macros"] }
serde = { version = "1.0", features = ["derive"] }
Development Dependencies
Dependencies needed only for development (tests, benchmarks) go in
[dev-dependencies]:
[dev-dependencies]
mockito = "1.0"
criterion = "0.5"
[build-dependencies]. These are used by build scripts in
build.rs.
Using External Crates
Once you've added a dependency to Cargo.toml, you can use it in
your code:
// In Cargo.toml:
// [dependencies]
// serde = "1.0"
// In your code:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct User {
name: String,
age: u32,
}
Publishing to crates.io
To publish your crate to crates.io:
- Create an account: Sign up at crates.io and get an API token
- Add metadata: Ensure your
Cargo.tomlhas required fields:[package] name = "my_crate" version = "0.1.0" edition = "2021" description = "A short description" license = "MIT OR Apache-2.0" repository = "https://github.com/user/repo" - Publish: Run
cargo publish
Workspaces
A workspace is a set of packages that share a Cargo.lock and output
directory:
# Cargo.toml at workspace root
[workspace]
members = [
"crate1",
"crate2",
"crate3",
]
[workspace.dependencies]
shared_dep = "1.0"
cargo build --workspace.
When to Use Packages vs Crates
Use a package when:
- Creating a new project: Use
cargo newto create a package - Organizing code: Group related functionality into packages
- Sharing code: Publish packages to crates.io
- Managing dependencies: Packages manage their own dependencies
Use multiple crates when:
- Binary + library: Have both executable and reusable code
- Multiple binaries: Need several executables in one package
- Workspace: Managing multiple related packages
Best Practices
- Use semantic versioning: Follow MAJOR.MINOR.PATCH versioning
- Specify exact versions carefully: Use
^for compatibility - Keep dependencies up to date: Use
cargo updateregularly - Use features wisely: Only enable features you need
- Document your crate: Add README.md and documentation
- Test before publishing: Run
cargo testandcargo build --release - Use workspaces for monorepos: Manage multiple packages together
Common Mistakes
1. Not specifying edition in Cargo.toml
// Wrong - Missing edition
[package]
name = "my_project"
version = "0.1.0"
// Correct - Specify edition
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
2. Using wildcard versions for dependencies
// Wrong - Too permissive
[dependencies]
serde = "*" // Can break with any update
// Correct - Specify compatible version
[dependencies]
serde = "1.0" // Compatible with 1.x.x
3. Forgetting to add dependencies to Cargo.toml
// Wrong - Using crate without adding to Cargo.toml
use serde::Serialize; // Error: can't find crate
// Correct - Add to Cargo.toml first
// [dependencies]
// serde = "1.0"
use serde::Serialize; // OK
4. Not running cargo build after adding dependencies
# Wrong - Dependencies not downloaded
# Added to Cargo.toml but didn't run cargo build
# Correct - Build to download dependencies
cargo build
Exercise: Packages & Crates Practice
Task: Practice creating and managing packages and dependencies.
Requirements:
- Create a Cargo.toml file with proper metadata
- Add dependencies from crates.io
- Understand the difference between binary and library crates
- Learn about different dependency sources
Click Run to execute your code
Show Solution
# Cargo.toml
[package]
name = "calculator"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = "1.0"
serde_json = "1.0"
# Binary crate: src/main.rs (has main function)
# Library crate: src/lib.rs (no main function)
# Package: Contains Cargo.toml and one or more crates
# Dependency sources:
# 1. From crates.io: serde = "1.0"
# 2. From Git: my_crate = { git = "https://github.com/user/repo" }
# 3. From local: my_crate = { path = "../my_crate" }
Summary
- Crate is the smallest compilation unit (binary or library)
- Package is one or more crates with a Cargo.toml
- Binary crate has
main()and produces executable - Library crate has no
main()and provides functionality - Cargo.toml defines package metadata and dependencies
- Dependencies can come from crates.io, Git, or local paths
- Use
^version specifier for compatibility - Features allow optional functionality in crates
- Workspaces manage multiple related packages
- Publish to crates.io to share your crates
What's Next?
Now that you understand packages and crates, you'll learn about iterators - one of Rust's most powerful features for processing collections. Iterators allow you to write efficient, functional-style code that's both readable and performant.
Enjoying these tutorials?