Lesson 10 of 12

Modules, Crates & Cargo

Learn how to split code into , use crates from crates.io, and manage builds, tests and dependencies with Cargo.

Bash
cargo new myapp           # binary project
cargo new mylib --lib      # library project
cargo add serde --features derive
cargo add tokio --features full

cargo run                  # build + run (debug)
cargo build --release      # optimized build in target/release
cargo test                 # run all tests
cargo check                # type-check only, much faster than build
cargo clippy               # the linter. Listen to it, it is usually right.
cargo fmt                  # canonical formatting
cargo doc --open           # build and read your own API docs

# Cargo.toml
# [package]
# name = "myapp"
# version = "0.1.0"
# edition = "2021"
#
# [dependencies]
# serde = { version = "1", features = ["derive"] }
# anyhow = "1"
#
# [dev-dependencies]
# criterion = "0.5"
Rust
// src/lib.rs — the crate root
pub mod store {                     // an inline module
    #[derive(Debug)]
    pub struct Item {
        pub name: String,           // pub on the field too, or it stays private
        quantity: u32,              // private to this module
    }

    impl Item {
        pub fn new(name: &str, quantity: u32) -> Self {
            Self { name: name.to_string(), quantity }
        }
        pub fn quantity(&self) -> u32 { self.quantity }
    }

    pub mod pricing {               // nested module
        pub fn with_tax(amount: f64) -> f64 { amount * 1.23 }

        pub(crate) fn internal_rate() -> f64 { 0.23 }   // visible inside this crate only
    }
}

// Bringing things into scope
use store::{Item, pricing::with_tax};

pub fn demo() {
    let item = Item::new("keyboard", 3);
    println!("{} x{} -> {:.2}", item.name, item.quantity(), with_tax(79.0));
}

// File-based modules: "mod store;" in lib.rs looks for
//   src/store.rs   or   src/store/mod.rs
// and nested modules live in src/store/pricing.rs
Rust
/// Adds two numbers.
///
/// # Examples
///
/// ```
/// assert_eq!(myapp::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }

pub fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 { return Err("division by zero".into()); }
    Ok(a / b)
}

// Unit tests live in the same file, in a module compiled only for tests
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adds_numbers() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn divides() {
        assert!(divide(1.0, 0.0).is_err());
        assert_eq!(divide(10.0, 4.0).unwrap(), 2.5);
    }

    #[test]
    #[should_panic(expected = "index out of bounds")]
    fn panics_on_bad_index() {
        let v: Vec<i32> = vec![];
        let _ = v[0];
    }
}

// Integration tests go in tests/*.rs and see only the PUBLIC API.
// Doc examples in /// comments are compiled and run by "cargo test" too.
Finished reading?Mark it done to track your progress.

Tip: use the ← and → keys to move between lessons.