17.2 Crates: The Building Blocks of Rust

A crate is Rust’s fundamental unit of compilation. Each crate compiles independently, which means Rust can optimize and link crates with a high degree of control. The compiler treats each crate as either a library (commonly .rlib) or an executable.

17.2.1 Binary and Library Crates

  • Binary Crate: Includes a main() function and produces an executable.
  • Library Crate: Lacks a main() function, compiling to a .rlib (or a dynamic library format if configured). Other crates import this library crate as a dependency.

By default:

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

17.2.2 The Crate Root

The crate root is the initial source file the compiler processes. Modules declared within this file (or in sub-files) form a hierarchical tree. You can refer to the crate root explicitly with the crate:: prefix.

17.2.3 External Crates and Dependencies

You specify dependencies in your Cargo.toml under [dependencies]:

[dependencies]
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }

After this, you can bring external items into scope with use:

use rand::Rng;

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

The Rust standard library (std) is always in scope by default; you don’t need to declare it in Cargo.toml.

17.2.4 Legacy extern crate Syntax

Prior to Rust 2018, code often used extern crate foo; to make the crate foo visible. With modern editions of Rust, this step is unnecessary—Cargo handles this automatically using your Cargo.toml entries.