Cargo, Crates and Basic Project Structure

Cargo

Cargo is Rust’s built-in package manager and build system. It also supports the following actions,

CommandAction
cargo newCreate a new project
cargo initCreate a new project in an existing directory
cargo checkVerify the project compiles without errors
cargo buildBuild the executable
cargo runBuild the executable and run

💡 The cargo check command verifies that the project compiles without errors, without producing an executable. Thus, it is often faster than cargo build.

💡 Cargo places executables compiled with cargo build or cargo run in the target/debug/ directory. But, while those built with cargo build --release for release purposes are stored in target/release/ directory. Release builds use more optimizations and remove some runtime safety checks to increase performance, although this comes at the cost of longer compile time.

CommandAction
cargo addAdd a dependency crate to the project
cargo removeRemove a dependency crate from the project
cargo fetchDownload the dependencies specified in Cargo.lock
cargo updateUpdate project dependencies

💡 A crate is a package that can be shared via crates.io, Rust community’s crate registry. cargo add, cargo remove, cargo fetch, and cargo update commands manage project dependencies through the crate hosted on crates.io.

💡 The cargo add command includes a specified crate in the [dependencies] section of Cargo.toml, while cargo add --dev adds a crate to the [dev-dependencies] section. This indicates that the crate is only used for development purposes like testing and will not be included in the final compiled code.

CommandAction
cargo testRun tests
cargo benchRun benchmarks
cargo docGenerate the project documentation via rustdoc

In addition, there are cargo commands to publish the project as a crate to crates.io.

CommandAction
cargo loginLogin to crates.io with the API token
cargo packageMake the local crate uploadable to crates.io
cargo publishUpload the crate to crates.io
cargo installInstall a Rust binary
cargo uninstallUninstall a Rust binary

💡 You need to get an API token from crates.io to publish a crate to it. The API token can be found in the Account Settings page, after login to that site. We will discuss more about this under code organization with crates.

Crate

  • A crate is a package, which can be shared via Rust community’s crate registry, crates.io.

  • A crate can produce an executable or a library. In other words, it can be a binary crate or a library crate.

    1. cargo new crate_name --bin or cargo new crate_name: Produces an executable
    2. cargo new crate_name --lib: Produces a library

The first one generates,

├── Cargo.toml
└── src
    └── main.rs

and the second one generates,

├── Cargo.toml
└── src
    └── lib.rs
  • Cargo.toml(capital c) is the configuration file which contains all of the metadata that Cargo needs to compile your project.
  • src folder is the place to store the source code.
  • Each crate has an implicit crate root/ entry point. main.rs is the crate root for a binary crate and lib.rs is the crate root for a library crate.

Project Structure

This is how Cargo documentation describes about the recommended project layout,

.
├── Cargo.toml
├── Cargo.lock
├── src
│   ├── main.rs
│   ├── lib.rs
│   └── bin
│       ├── another_executable.rs
│       └── multi_file_executable
│           ├── main.rs
│           └── some_module.rs
├── tests
│   └── some_integration_tests.rs
├── benches
│   └── simple_bench.rs
└── examples
    └── simple_example.rs
  • The source code goes in the src directory.
    • The default executable file is src/main.rs.
    • The default library file is src/lib.rs.
    • Other executables can be placed in,
      • src/bin/*.rs
      • src/bin/*/main.rs
  • Integration tests go in the tests directory (unit tests go in each file they’re testing).
  • Benchmarks go in the benches directory.
  • Examples go in the examples directory.

Rust Editions

Rust guarantees backward compatibility while introducing major updates to the language. To support this, the edition field was added to the Cargo.toml file in Rust 2018, marking the first major update to the language ecosystem three years after its initial release. Editions are opt-in, meaning existing crates will not experience these changes until they explicitly migrate to the new edition.

The major editions of Rust are:

  • Rust 2015: The initial edition, introduced with Rust 1.0. It established the core language features like ownership, borrowing, and lifetimes, laying the foundation for Rust’s safety and concurrency guarantees.

  • Rust 2018: The first major update, introduced the edition field in Cargo.toml, simplified the module system, stabilized async/await, improved error handling with the ? operator, and made several syntactic changes.

  • Rust 2021: Focused on improving ergonomics and removing inconsistencies, such as disjoint closure capture, IntoIterator for arrays, and the introduction of or-patterns in macros.

  • Rust 2024: The latest edition, includes enhancements like refined async features, more const generics, better diagnostics, and improved Cargo features.

For new projects created by cargo new, it will set edition = "2024" by default in the Cargo.toml file. For example,

[package]
name = "hello_world"
version = "0.1.0"
edition = "2024"

👨‍🏫 Before going to the next…

  • The .cargo/bin directory of your home directory is the default location of Rust binaries. Not only the official binaries like rustc, cargo, rustup, rustfmt, rustdoc, rust-analyzer and also the binaries you can install via cargo install command, will be stored in this directory.

  • Even though the initial convention for naming crates and file names is using the snake_case, some crate developers are using kebab-case on both crates and file names. To make your code more consistent, use the initial convention snake_case; especially on file names.

  • Create an executable crate via cargo new command and run it via cargo run.

  • Create a library crate via cargo new command and run cargo test.