Skip to content

← Modules, Visibility, Testing and Docs step 1 of 22

Easy Primitives

Modules are a tree you declare, not files you discover

If you come from Python, Go or JavaScript you have an instinct that says the filesystem is the module graph. Drop a file in a directory and it is importable; delete it and it isn’t. Rust does not work that way, and this is the first thing to unlearn.

In Rust the module graph is declared. A mod item creates a module; the filesystem, at most, supplies a body for one. A file that nobody has written a mod statement for is not part of your crate at all — the compiler will never open it, and you will spend twenty minutes wondering why your new code has no effect. (Track item 10.11 covers where those bodies live on disk. Here there is no disk: every module is an inline block.)

Two functions are graded.

pub fn module_paths() -> Vec<String>
pub fn fn_local_path() -> String

Build exactly three nested modules — geometry, geometry::area, and geometry::area::planar — each exposing

pub fn here() -> &'static str { module_path!() }

module_paths returns those three paths, outermost first, with the leading crate segment stripped. fn_local_path does the same for a module named hidden that you declare inside a function body.

Why you have to strip the first segment

module_path!() is a built-in macro that expands, at compile time, to the full path of the module it appears in — including the crate name. On this site your submission is compiled as a single file, so the crate is named after that file. If you returned the raw string, the correct answer would depend on a filename you do not control. Strip everything up to and including the first :: and the answer becomes a property of your tree, which is the thing being taught.

A module inside a function

You can write mod hidden { … } in the middle of a function body. Two things about it surprise people, and the second is the interesting one.

  • The function name does not appear in the path. mod hidden inside fn fn_local_path has the module path crate::hidden — it looks exactly like a module declared at the top of the file.
  • Nevertheless it is not reachable from anywhere outside that function body. Its path is crate-level, its scope is the block. A test asserts that it is not one of the three paths module_paths returns.

Function-local modules are rare, but they are the cleanest demonstration that a module is a namespace node, not a file and not a scope in the ordinary sense.

What you will meet on the way

The starter is missing the innermost module and calls it anyway, so it fails to compile with E0433 — failed to resolve. Read that error before you fix it. E0433 always means “this name is not in the tree”, never “this name is private”; the second one is a different error (E0603) and a different lesson, coming up in 10.3.

Two more things worth knowing now:

  • Items in a block are order-independent. module_paths can call geometry::here() even though mod geometry is written below it. Functions, structs, modules, constants — all of them. macro_rules! is the one glaring exception, and item 10.10 is entirely about the day that bites you.
  • A private item nobody uses trips dead_code, which -D warnings turns into a failure. The fix is never #[allow(dead_code)]. Either use the item or delete it; if you genuinely need it visible further up the tree, that is what pub(crate) is for.