17.2 Crates: The Building Blocks of Rust Projects

Crates are the fundamental units of code compilation and distribution in Rust.

17.2.1 What Is a Crate?

A crate is the smallest unit of code that the Rust compiler considers at a time. It is either a binary or a library and forms a module tree starting from a crate root.

17.2.2 Binary and Library Crates

  • Binary Crates: Generate executables and must have a main function. They are the entry points for programs.
  • Library Crates: Provide reusable functionality and do not have a main function. They produce .rlib files and can be included as dependencies.

Example:

  • Binary Crate: src/main.rs
  • Library Crate: src/lib.rs

17.2.3 The Crate Root

The crate root is the starting point of compilation for any Rust crate. It is the source file that defines the module hierarchy and links to the rest of the code in the crate.

For binary crates, the crate root is typically src/main.rs, serving as the entry point of the executable program.
For library crates, the crate root is src/lib.rs, providing the public API for the library.

The crate root establishes an implicit (or virtual) root module named crate, into which the entire source code of the crate is embedded. This virtual module serves as a global namespace for the crate. To reference items at the top level of the crate from within submodules, the crate:: prefix can be used.

17.2.4 External Crates

External crates allow you to integrate third-party libraries into your Rust project. These crates are managed by Cargo and are typically hosted on crates.io.

Declaring Crates in Cargo.toml

Add dependencies in the [dependencies] section:

[dependencies]
rand = "0.8"     # Version 0.8 of the rand crate
serde = { version = "1.0", features = ["derive"] }  # With features

Using External Crates in Code

After declaring the dependency, you can bring external crates into scope using the use keyword:

use rand::Rng;
fn main() {
    let mut rng = rand::thread_rng();
    let n: u32 = rng.gen_range(1..101);
    println!("Generated number: {}", n);
}

Note that the standard library std is also a crate that's external to our package. Because the standard library is shipped with the Rust compiler, we don't have to list std in Cargo.toml. But we do need to refer to it with use to bring items from there into our package's scope. For example, with HashMap we would use this line:

#![allow(unused)]
fn main() {
use std::collections::HashMap;
}

17.2.5 The extern crate Keyword (Legacy)

In earlier versions of Rust, the extern crate keyword was required to bring external crates into scope, as in extern crate rand;. As of the 2018 edition, this is no longer necessary for most cases, and you can use external crates directly with use.