Cargo, Crates and Basic Project Structure

Cargo

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

Command Action
cargo new Create a new project
cargo init Create a new project in an existing directory
cargo check Verify the project compiles without errors
cargo build Build the executable
cargo run Build 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.

Command Action
cargo add Add a dependency crate to the project
cargo remove Remove a dependency crate from the project
cargo fetch Download the dependencies specified in Cargo.lock
cargo update Update 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.

Command Action
cargo test Run tests
cargo bench Run benchmarks
cargo doc Generate the project documentation via rustdoc

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

Command Action
cargo login Login to crates.io with the API token
cargo package Make the local crate uploadable to crates.io
cargo publish Upload the crate to crates.io
cargo install Install a Rust binary
cargo uninstall Uninstall 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

The language has seen a series of improvements every three years through new editions since its initial stable release in 2015, including the initial version, Rust 2015, followed by Rust 2018, and the latest, Rust 2021.

The edition key in the Cargo.toml file denotes the edition of the Rust compiler to be used for compiling the crate. Editions are opt-in, meaning existing crates will not see these changes until they explicitly migrate to the new edition. Rust guarantees backward compatibility between editions, allowing crates using older editions of Rust to interoperate seamlessly with those using newer versions.

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

[package]
name = "api"
version = "0.1.0"
edition = "2021"

👨‍🏫 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.