Apr 29, 2025
Rust is known for its strong type system, memory safety guarantees, and emphasis on correctness. But a programming language is only part of the development experience. Developers also need tools to create projects, manage dependencies, compile code, run tests, and distribute applications.
In Rust, the tool that brings these tasks together is Cargo.
Cargo is Rust’s build system and package manager. It provides a consistent workflow for developing projects, whether you are writing a small command-line utility or maintaining a large application with many dependencies.
The easiest way to start a new Rust project is with cargo new.
cargo new hello-world
cd hello-world
Cargo creates a directory containing the basic project structure:
hello-world/
├── Cargo.toml
└── src/
└── main.rs
The Cargo.toml file contains project metadata and configuration. The src/main.rs file contains the application’s entry point.
A newly generated application looks like this:
fn main() {
println!("Hello, world!");
}
For an existing directory, cargo init can initialize a Cargo project without creating a new directory.
Cargo also supports library projects:
cargo new my-library --lib
A library uses src/lib.rs as its main source file, making its code available for other Rust programs to depend on.
The Cargo.toml file is the manifest of a Rust package.
A typical manifest might look like this:
[package]
name = "hello-world"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }
The [package] section describes the package, including its name, version, and Rust edition.
The [dependencies] section declares external crates that the project needs.
For example, serde is a widely used serialization framework. Declaring it in the manifest allows Cargo to resolve the dependency and make it available to the project.
Dependencies do not need to be downloaded or configured manually one by one. Cargo handles dependency resolution as part of its normal workflow.
One of Cargo’s biggest advantages is that dependency management is integrated with the build process.
You can add a dependency directly to Cargo.toml, or use:
cargo add serde
To remove a dependency, you can use:
cargo remove serde
Cargo resolves the dependency graph, downloads the necessary packages, and compiles them when needed.
When Cargo resolves dependencies, it records the selected versions in Cargo.lock.
This file helps ensure that subsequent builds use the same resolved dependency versions rather than independently resolving potentially different versions each time.
For applications and executable projects, Cargo.lock should generally be committed to version control.
For libraries, the decision is different: the lockfile is useful for developing and testing the library itself, but downstream consumers resolve dependencies for their own projects.
A dependency declaration such as:
[dependencies]
serde = "1"
does not mean that only version 1.0.0 is allowed. Cargo interprets it as a version requirement that permits compatible releases within the specified range.
Cargo’s dependency resolution follows its version-requirement rules, while Cargo.lock records the particular versions selected for a project.
Understanding the distinction between a version requirement and a lockfile is essential when maintaining reproducible builds.
Cargo provides a small set of commands for the everyday development cycle.
cargo run
Cargo compiles the project if necessary and then executes the resulting program.
cargo build
By default, this creates a development build with debugging information and limited optimization.
For a release build, use:
cargo build --release
Release builds enable optimizations and are generally more suitable for performance-sensitive applications.
The output is placed in the target directory. Development and
Related tags: