Coding by Hand
Rust home

Reading Rust Code

A Rust program is a library, and modules are its branches. The main branch holds the front desk. Each subject sits in its own room — fiction in one, science in another, history down the hall. Books inside a room are private to that room until a librarian hangs a pub sign on the door. The whole point of the system is that you can walk into any branch and find what you need without reading every book to know what is there. Once you can read the floor plan, you can read any Rust crate on the internet.

A floor plan of a small city library showing branches as Rust modules.
A floor plan of a small city library showing branches as Rust modules.

The idea of carving a program into rooms is older than Rust by half a century. In 1975 a Swiss professor named Niklaus Wirth shipped a language called Modula that did one new thing well — it let you put related functions in a box called a module and then say which parts of the box were public. Before that, every function in a program could see every other function, which worked for small programs and turned into a swamp for large ones. Wirth's idea moved through Ada in 1983, through Standard ML in the late 80s, and finally into Rust when Graydon Hoare started the project at Mozilla in 2006. Hoare borrowed the word mod straight from those ancestors. The keyword has not changed since.

Here is the smallest example of the floor plan. One file, two branches, a couple of books each. The mod keyword carves a room out of the file. The pub keyword unlocks the door.

mod library {
    pub mod fiction {
        pub fn pick() -> &'static str {
            "Dune"
        }
    }
    pub mod science {
        pub fn pick() -> &'static str {
            "The Selfish Gene"
        }
    }
}

Inside the file, you call library::fiction::pick() and Rust walks the path for you — library, then fiction, then the function called pick. The :: is the hallway. Each pair of colons walks you one door deeper into the building. If you had to type that full path every time you wanted a book, your code would be unreadable. Which is why Rust gives you the catalog card.

use library::fiction;
use library::science::pick as pick_science;

A use statement is a card you tape above your desk that says "when I write fiction, I mean library::fiction." Now any call to fiction::pick() knows where to go. The as keyword lets you rename a card when two branches have a function with the same name — pick from science becomes pick_science so you never confuse the two. Real Rust files start with a stack of these cards, ten or twenty deep, each one a shortcut into a different room of a different crate. When you open a file in someone else's project, read the use block first. It is the table of contents.

A library catalog card showing how a use statement maps a short name to a full module path.
A library catalog card showing how a use statement maps a short name to a full module path.

One question you should be asking — if every function lives in some module, why does the next line work without a use for Vec or String or println!?

fn main() {
    let novel = fiction::pick();
    let textbook = pick_science();
    let shelf: Vec<String> = vec![String::from(novel), String::from(textbook)];
    println!("today's shelf:");
    for title in &shelf {
        println!("  - {title}");
    }
    println!("two books, two modules, zero imports for Vec or String.");
}

The answer is the prelude, and it is the third piece you need to read Rust. The prelude is a tiny list of the most-used items in the whole standard library, and Rust silently imports them into every file you write. String, Vec, Option, Result, Box, println!, Iterator — all in the prelude. The idea came from Haskell, which had a Prelude module that was always in scope. Rust copied it because writing use std::vec::Vec; at the top of every file would have driven everyone insane. The prelude is the reading room at the front of the library — the books are out on the tables, no card needed.

Run the program and you see all three pieces work together — the modules, the use cards, the silent prelude — to print two book titles from two different branches.

today's shelf:
  - Dune
  - The Selfish Gene
two books, two modules, zero imports for Vec or String.

Now zoom out. The crate everyone in the Rust world uses, serde, has its functions and types spread across dozens of modules — serde::ser, serde::de, serde::de::value, on and on. The first time you open the docs, the tree looks like a hedge maze. It is not. It is the same floor plan as the toy library above, just bigger. Open docs.rs/serde, find the module tree on the left, click ser, and you see Serializer, SerializeSeq, SerializeStruct. Those are the books in that room. At the top of any file that uses serde, you will see use serde::{Serialize, Deserialize}; — the catalog cards for the two most common books, lifted from one of those rooms by David Tolnay and Erick Tryzelaar when they shipped serde in 2014. They picked the path, you walk it.

The serde crate's module tree, drawn as nested branches with each leaf a public type.
The serde crate's module tree, drawn as nested branches with each leaf a public type.

Next lesson — what actually goes inside the rooms once you build them, starting with how Rust binds a value to a name and lets you reuse that name at a new type.