Cogs and Levers A blog full of technical stuff

Learning Rust Part 11 - Crates and Package Management

Introduction

Rust’s package manager, Cargo, provides an all-in-one toolset for building, dependency management, testing, and more. Beyond managing individual projects, Cargo also supports multi-package workspaces, making it ideal for complex Rust applications. With additional tools like Clippy for linting and Rustfmt for formatting, Cargo enables streamlined package development and code maintenance.

Cargo Basics (Build System and Package Manager)

Cargo serves as Rust’s build system and package manager, handling tasks from project creation to compiling, testing, and managing dependencies. Each project includes a Cargo.toml file, which defines package metadata, dependencies, and configurations.

Creating a New Project

To start a new Rust project, use cargo new, which sets up a folder with a Cargo.toml file, src/main.rs or src/lib.rs, and other necessary project files.

cargo new my_project --bin    # Creates a binary project
cargo new my_library          # Creates a library project

Building and Running

Cargo provides commands for compiling and running Rust projects, ensuring an efficient development cycle.

cargo build           # Compiles the project
cargo run             # Builds and runs the project
cargo build --release # Builds an optimized release version

Cargo Workspaces

Cargo workspaces allow you to manage multiple interdependent packages within a single project, making it easier to develop complex applications with multiple crates.

Creating a Workspace

Define a workspace by creating a Cargo.toml at the project’s root and specifying member crates. Each member crate has its own folder with its own Cargo.toml.

# Cargo.toml
[workspace]
members = ["crate1", "crate2"]

With this setup, you can run cargo build or cargo test for all workspace members at once, simplifying multi-crate development.

Dependencies and Versioning

Cargo simplifies dependency management with the [dependencies] section in Cargo.toml. You can specify dependencies by version, Git repository, or local path.

Adding Dependencies

Add dependencies to Cargo.toml, and Cargo will download and build them automatically.

[dependencies]
serde = "1.0"                      # Version from crates.io
rand = { version = "0.8" }         # Alternate syntax for version
my_local_crate = { path = "../my_local_crate" } # Local dependency

Semantic Versioning

Cargo follows semantic versioning (major.minor.patch) for specifying compatible versions.

serde = "1.0"  # Compatible with 1.0 or higher, but below 2.0
serde = "~1.0" # Compatible with 1.0.x only

Publishing to Crates.io

Publishing a crate to crates.io makes it available to the Rust community. To publish, create an account on crates.io and generate an API token.

Steps to Publish

  1. Update Cargo.toml: Include essential information like name, description, license, and repository link.
  2. Login and Publish: Use cargo login with your API token, then cargo publish to upload the crate.
cargo login <API_TOKEN>
cargo publish

Versioning for Updates

After publishing, increment the version in Cargo.toml before publishing updates. Follow semantic versioning rules for breaking changes, new features, and patches.

version = "1.1.0" # Update for new features

Rust Toolchain Management (rustup and cargo-install)

Rustup manages Rust’s toolchain, making it easy to install, update, or switch between versions. Rustup supports stable, beta, and nightly versions of Rust.

Using Rustup

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Update Rust
rustup update

# Switch to the nightly toolchain
rustup default nightly

Installing Packages Globally with cargo install

cargo install allows you to install Rust binaries globally, useful for tools like Clippy or custom Rust tools from GitHub.

cargo install ripgrep     # Install ripgrep, a fast search tool
cargo install cargo-edit  # Install a cargo subcommand from GitHub

Clippy for Linting

Clippy is Rust’s linter, designed to catch common mistakes, stylistic issues, and potential bugs. Run Clippy with cargo clippy, and it will analyze your code for possible improvements.

Using Clippy

If Clippy isn’t already installed, add it as a component.

rustup component add clippy
cargo clippy

Clippy provides suggestions with severity levels like “warning” and “help,” encouraging idiomatic and optimized Rust code. For instance, Clippy might recommend avoiding redundant clones or inefficient operations.

Rustfmt for Code Formatting

Rustfmt automatically formats Rust code according to Rust’s style guide, ensuring consistency across the codebase. Rustfmt is especially useful in collaborative projects and CI pipelines.

Formatting with Rustfmt

Run Rustfmt with cargo fmt to format your code in place, following Rust’s official style guide.

rustup component add rustfmt
cargo fmt

Rustfmt can also be customized with a .rustfmt.toml file, where you can set options for indentation, line width, and more.

# .rustfmt.toml
max_width = 100  # Set max line width
hard_tabs = false

Summary

Rust’s Cargo package manager and associated toolchain provide an efficient approach to project management, dependency handling, and distribution. Cargo workspaces simplify managing multi-crate projects, while tools like Clippy and Rustfmt maintain code quality and style. With support for publishing and version control, Cargo and Rust’s ecosystem streamline the development, distribution, and maintenance of reliable Rust projects.