RustArena

Step 2 of 7

Paths: crate, super and self

A path names an item. There are two kinds:

The crate root is the top of the module tree: the file main.rs (or lib.rs) itself. Everything you write at the top level, like fn main, lives there. The module tree of the next example:

crate                 (main.rs, the crate root)
├── fn log
├── fn main
└── mod restaurant
    ├── fn open
    ├── fn ring_bell
    └── mod kitchen
        ├── fn cook
        └── fn prep
fn log(msg: &str) {
    println!("[log] {msg}");
}

mod restaurant {
    pub fn open() {
        crate::log("opening");   // absolute: from the crate root
        kitchen::cook("soup");   // relative: kitchen is a child of restaurant
    }

    fn ring_bell() {
        println!("ding!");
    }

    pub mod kitchen {
        pub fn cook(dish: &str) {
            prep(dish);          // same module: just the name
            println!("{dish} is ready");
            super::ring_bell();  // the parent module, restaurant
        }

        fn prep(dish: &str) {
            println!("chopping for {dish}");
        }
    }
}

fn main() {
    restaurant::open();
    restaurant::kitchen::cook("salad");
}
[log] opening
chopping for soup
soup is ready
ding!
chopping for salad
salad is ready
ding!

Which kind should you write? Absolute crate:: paths keep working when you move the code that uses them; relative super:: paths keep working when you move a module together with its parent. Many teams prefer crate:: in general for clarity, and super:: for close relatives, most famously use super::*; in test modules (the testing module).

Common mistake: names from outside the module

A module does not see the names of the scope around it, unlike a block inside a function:

fn log(msg: &str) {
    println!("[log] {msg}");
}

mod restaurant {
    pub fn open() {
        log("opening");
    }
}

fn main() {
    restaurant::open();
}
error[E0425]: cannot find function `log` in this scope

The compiler suggests consider importing this function: use crate::log;. Either write the path (crate::log("opening") or super::log(...)) or bring the name in with use, the next step.

Your task

The starter doesn't compile: inside teller::withdraw, the calls to fee and audit can't find those functions. Give each call the right path: fee lives in the parent module bank (use super::), and audit lives in the crate root (use crate::). The program should print:

audit: withdrawal
balance: 68

Challenge 2

Fill in the two path prefixes: heal (in game::player) needs max_hp from its parent module game, and price_with_bonus (in shop) needs bonus from the crate root. The program prints:

80
100
15

Challenge 3

Build this module tree. shout is given at the root.

crate
├── fn shout(s: &str) -> String       (given: uppercases and adds "!")
└── mod notify
    ├── pub fn alert(msg: &str) -> String
    │       "[ALERT] " followed by shout(msg)
    └── pub mod email
        └── pub fn subject(msg: &str) -> String
                "Subject: " followed by alert(msg)

So notify::alert("disk full") is "[ALERT] DISK FULL!" and notify::email::subject("disk full") is "Subject: [ALERT] DISK FULL!". Reach shout and alert with paths (crate:: or super::); don't copy their code.

Previous: Modules and privacy Next: use: shortcuts for long paths