Managing growing projects with packages, crate and modules
These features, sometimes collectively referred to as the module system, include:
- 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
Packages and crates
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
When we entered the command, Cargo created a Cargo.toml file, giving us a package. Looking at the contents of Cargo.toml, there’s no mention of src/main.rs because 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.
Modules to control scope and privacy
// src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
// module tree for the above structure:
// crate
// └── front_of_house
// ├── hosting
// │ ├── add_to_waitlist
// │ └── seat_at_table
// └── serving
// ├── take_order
// ├── serve_order
// └── take_payment
Paths for referering to an item in the module tree
A path can take two forms:
- An absolute path starts from a crate root by using a crate name or a literal
crate
. - A relative path starts from the current module and uses
self
,super
, or an identifier in the current module.
mod front_of_house {
// exposing paths with the `pub` keyword:
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
// calling a function using a relative path starting with super
fn serve_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::serve_order();
}
fn cook_order() {}
}
// making structs and enums public
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub fn eat_at_restaurant() {
// Order a breakfast in the summer with Rye toast
let mut meal = back_of_house::Breakfast::summer("Rye");
// Change our mind about what bread we'd like
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
// The next line won't compile if we uncomment it; we're not allowed
// to see or modify the seasonal fruit that comes with the meal
// meal.seasonal_fruit = String::from("blueberries");
}
// for public enum, all of its variantes are then public
mod back_of_house {
pub enum Appetizer {
Soup,
Salad,
}
}
pub fn eat_at_restaurant() {
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}
Bringing paths into scope with the use
keyword
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// idiomatic `use` paths
use crate::front_of_house::hosting;
// or in relative path
// use self::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
// providing new names with the `as` keyword
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
Ok(())
}
fn function2() -> IoResult<()> {
// --snip--
Ok(())
}
// re-exporting names with `pub use`
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// By using pub use, external code can now call the add_to_waitlist function
// using hosting::add_to_waitlist. If we hadn’t specified pub use, the
// eat_at_restaurant function could call hosting::add_to_waitlist in its scope,
// but external code couldn’t take advantage of this new path.
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
// If we want to bring all public items defined in a path into scope, we can
// specify that path followed by *, the glob operator:
use std::collections::*;
Using external packages
Adding rand as a dependency in Cargo.toml tells Cargo to download the rand package and any dependencies from crates.io and make rand available to our project.
Seperating modules into different files
// src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
// src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
}
// src/front_of_house.rs
// use a semicolon rathat than a block tells Rust to load the contents of the module
// from another file with the same name as the module
pub mod hosting;
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}
}