13.xChapter 13 summary and quiz

Last updated June 13, 2026

This chapter was about organization rather than new language features, and it quietly retired an entire C++ chapter's worth of ceremony. Review, then a quiz.

Quick review

As programs grow, names collide (13.1), and modules are named scopes that prevent it: a struct groups related values, a module groups related code. You define modules with mod, nest them into a tree rooted at crate, and name items with paths (13.2): absolute from crate::, relative from the current module or via self::/super::. use brings a path into scope to shorten it (the convention: import a function's parent module, but import types directly), and as renames to dodge collisions.

Everything is private by default (13.3); pub exposes an item to its parent, pub(crate) exposes it within your crate but not to external users, and struct fields are made public one at a time. Private-by-default plus per-field visibility is encapsulation: expose a deliberate surface, hide the internals so you can change them freely. Moving a module to its own file is one mod foo; declaration plus a foo.rs file (13.4); nested modules become directories. This single mechanism replaces C++'s headers, header guards, forward declarations, and preprocessor includes, because mod names a module rather than pasting text.

A crate is a compile unit; a package (from cargo new) holds one or more crates (13.5). A binary crate (src/main.rs, has main) compiles to an executable; a library crate (src/lib.rs, no main) provides code for others. Putting logic in lib.rs and keeping main.rs thin makes the logic testable and reusable, which chapter 14 depends on. Cargo (13.6) reads Cargo.toml, builds with dev/release profiles, and offers check, test, doc, fmt, and clippy beyond run/build; workspaces and features exist for larger projects. Finally, doc comments (/// for the item below, //! for the enclosing item) build a docs site with cargo doc and run their examples as tests (13.7), and the matching skill is reading docs (13.8): a signature is a contract you can read, telling you what a function needs, returns, and whether it can fail.

Quiz time

Question #1

What's the difference between an absolute path and a relative path, and what keyword starts each?

Show solution

An absolute path names an item starting from the crate root and begins with crate::. A relative path starts from the current module: just the child's name, or self:: (current module) or super:: (parent module). Both name the same item by walking the module tree from different starting points.

Question #2

This is refused. Name the error and give two fixes.

mod math {
    fn square(n: i32) -> i32 { n * n }
}

fn main() {
    println!("{}", math::square(5));
}
Show solution

E0603: square is private to math, so main can't call it. Fix (1): mark it pub fn square. Fix (2): if square should stay internal, move the calling code inside math, or expose a different pub function that calls square. The cleanest fix here is pub fn square.

Question #3

You have a growing main.rs with an inline mod parser { ... } and an inline mod formatter { ... }. Describe the files you'd create to split both into their own files, and the one line each needs in main.rs.

Show solution

In main.rs, replace each module body with a declaration: mod parser; and mod formatter;. Create src/parser.rs with the parser module's contents and src/formatter.rs with the formatter's. The compiler finds each file from its mod line; paths and use in main.rs stay the same. Items the rest of the program calls must be pub.

Question #4

A small exercise. Sketch a library crate geometry with a public shapes module containing a public Circle struct (private radius field, a pub fn new, and a pub fn area), and a main.rs in the same package that creates a circle and prints its area. Show the file layout and the key pub/use decisions.

Show solution
geometry/
├── Cargo.toml          (name = "geometry")
├── src/
│   ├── lib.rs          declares the public module
│   ├── shapes.rs       the Circle type
│   └── main.rs         thin binary using the library
// src/lib.rs
pub mod shapes;
// src/shapes.rs
pub struct Circle {
    radius: f64,        // private: enforces use of `new`
}

impl Circle {
    pub fn new(radius: f64) -> Circle {
        Circle { radius }
    }

    pub fn area(&self) -> f64 {
        3.14159 * self.radius * self.radius
    }
}
// src/main.rs
use geometry::shapes::Circle;

fn main() {
    let c = Circle::new(2.0);
    println!("area: {}", c.area());
}
area: 12.56636

Key decisions: lib.rs re-exports shapes with pub mod so the binary can reach it. Circle and its methods are pub, but radius stays private, so outside code must go through new (lesson 13.3). main.rs reaches the type by the package name, geometry::shapes::Circle, because the binary is a separate crate depending on the library (13.5). All the logic lives in the library, so it's testable; main.rs is a thin shell.

Your code is now organized, split, and documented. Chapter 14 makes it trustworthy: Rust's built-in test framework, where #[test], assert_eq!, and the lib.rs split from this chapter combine into testing as an everyday habit rather than an afterthought.