Rust has a number of features that allow you to manage your code’s organization:
- Packages: A Cargo feature that lets you build, test, and share crates
- Crates: A tree of modules that produces a library or executable
- Modules and use: Let you control the organization, scope, and privacy of paths
- Paths: A way of naming an item, such as a struct, function, or module
crates
A crate is the smallest amount of code the rust compiler will compile at a
time. Even if you manually run rustc on a single source code file, rust will
consider that file to be a crate. A crate can come in the form of either a
library crate or a binary crate and can contain modules.
A binary crate has a main function and compiles to an executable. Library
crates instead are just used as libraries. Generally, the term crate by itself
is referring to library crates.
packages
A package is a bundle of one or more crates that provides a set of
functionality. A package contains a Cargo.toml file that describes how to
build those crates. A package can contain as many binary crates as you like, but
at most only one library crate. A package must contain at least one crate,
whether that’s a library or binary crate.
Cargo follows a convention that src/main.rs is the crate root of a binary
crate with the same name as the package. Likewise, Cargo knows that if the
package directory contains src/lib.rs, the package contains a library crate
with the same name as the package, and src/lib.rs is its crate root. Cargo
passes the crate root files to rustc to build the library or binary.
modules
You only need to load a file using a mod declaration once in your module tree. Once the compiler knows the file is part of the project (and knows where in the module tree the code resides because of where you’ve put the mod statement), other files in your project should refer to the loaded file’s code using a path to where it was declared. In other words, mod is not an “include” operation that you may have seen in other programming languages.
Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate or src/main.rs for a binary crate) for code to compile.
Declaring modules: In the crate root file, you can declare new modules; say you declare a “garden” module with mod garden;. The compiler will look for the module’s code in these places:
- Inline, within curly brackets that replace the semicolon following
mod garden - In the file
src/garden.rs - In the file
src/garden/mod.rs
Declaring submodules: In any file other than the crate root, you can declare
submodules. For example, you might declare mod vegetables; in src/garden.rs.
The compiler will look for the submodule’s code within the directory named for
the parent module in these places:
- Inline
- In the file
src/garden/vegetables.rs - In the file
src/garden/vegetables/mod.rs
Paths to code in modules: Once a module is part of your crate, you can refer
to code in that module from anywhere else in that same crate, as long as the
privacy rules allow, using the path to the code. For example, an Asparagus type
in the garden vegetables module would be found at
crate::garden::vegetables::Asparagus.
Private vs. public: Code within a module is private from its parent modules
by default. To make a module public, declare it with pub mod instead of mod.
To make items within a public module public as well, use pub before their
declarations.
The use keyword: Within a scope, the use keyword creates shortcuts to
items to reduce repetition of long paths. In any scope that can refer to
crate::garden::vegetables::Asparagus, you can create a shortcut with
use crate::garden::vegetables::Asparagus; and from then on you only need to
write Asparagus to make use of that type in the scope.
files
When modules get large, you might want to move their definitions to a separate file to make the code easier to navigate.