17.2 Crates: Rust’s Compilation Units
A crate is the fundamental unit passed to the Rust compiler (rustc
). Each crate is compiled independently, producing a single artifact (library or executable). This separation is key to Rust’s modularity, enabling separate compilation, effective optimization boundaries, and clear dependency management. Conceptually, a Rust crate is analogous to a single shared library (.so
, .dylib
), static library (.a
, .lib
), or executable produced by a C/C++ build process.
17.2.1 Binary vs. Library Crates
- Binary Crate: Compiles to an executable file. Its crate root must contain a
fn main() { ... }
function, which serves as the program’s entry point. - Library Crate: Compiles to a library format (e.g.,
.rlib
for static linking by default, or potentially dynamic library formats like.so
/.dylib
/.dll
if configured). It does not have amain
function entry point and is intended to be used as a dependency by other crates.
Cargo identifies crate roots by convention within the src/
directory:
src/main.rs
: Root of the main binary crate (sharing the package name).src/lib.rs
: Root of the library crate (sharing the package name).src/bin/name.rs
: Root of an additional binary crate namedname
.
17.2.2 The Crate Root and Module Tree
The crate root file (lib.rs
, main.rs
, etc.) is the entry point for the compiler within that crate. All modules defined within the crate form a hierarchical tree structure originating from this root file (see Section 17.3). The special path crate::
always refers to the root of the current crate’s module tree, allowing unambiguous access to items defined at the top level of the crate or in its modules.
17.2.3 Using External Crates (Dependencies)
To leverage code from external libraries (crates), you first declare them as dependencies in your package’s Cargo.toml
:
[dependencies]
# Dependency from crates.io (version "0.8.x", compatible with 0.8)
rand = "0.8"
# Dependency with specific features enabled
serde = { version = "1.0", features = ["derive"] }
# Dependency from a local path (e.g., within a workspace)
# my_local_lib = { path = "../my_local_lib" }
# Dependency from a Git repository
# some_crate = { git = "[https://github.com/user/repo.git](https://github.com/user/repo.git)", branch = "main" }
When you build your package, Cargo automatically downloads (if necessary), compiles, and links these dependency crates. Within your Rust code, you can then access items (functions, types, etc.) defined in a dependency crate using the use
keyword to bring them into scope:
// Import the `Rng` trait from the `rand` crate use rand::Rng; fn main() { // `rand::thread_rng()` returns a thread-local random number generator let mut rng = rand::thread_rng(); // `gen_range` is a method provided by the `Rng` trait let n: u32 = rng.gen_range(1..101); // Generates a number between 1 and 100 println!("Random number: {}", n); }
Note: The Rust Standard Library (std
) is implicitly linked and available. You don’t need to declare std
in Cargo.toml
. You access its components using paths like std::collections::HashMap
or by bringing them into scope with use std::collections::HashMap;
.
17.2.4 Historical Note: extern crate
In older Rust editions (specifically, Rust 2015), it was necessary to explicitly declare your intent to link against and use an external crate within your source code using extern crate crate_name;
at the crate root.
// Rust 2015 style - generally not needed in Rust 2018+
extern crate rand;
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n: u32 = rng.gen_range(1..101);
println!("Random number: {}", n);
}
Since the Rust 2018 edition, Cargo automatically handles this based on the [dependencies]
section in Cargo.toml
. The extern crate
declaration is now implicit and generally omitted, except for a few specific advanced use cases (like renaming crates globally or using macros from crates without importing other items). For C programmers, this change makes dependency usage feel slightly more like including a header that makes library functions available, but with the crucial difference that Cargo manages the actual linking based on Cargo.toml
.