Chapter 20

Modules and Visibility

👋 Anyone can read and edit this exercise. Sign up to save your progress.

Modules are how Rust organizes code into namespaces. They give you two things: a way to group related items together, and a way to control which of those items are visible from the outside.

The default is private. Add pub to expose something:

mod calculator {
    pub fn add(a: i32, b: i32) -> i32 {     // visible outside
        a + b
    }

    fn helper() -> i32 { 42 }               // private to this module
}

fn main() {
    let sum = calculator::add(1, 2);   // OK
    // calculator::helper();           // ERROR: private
}

You can declare a module inline (as above) or in a separate file. The syntax mod foo; (no body) tells the compiler to look for foo.rs or foo/mod.rs next to the current file.

Visibility for fields and variants

Marking a struct pub only makes the type public. The fields are still private unless individually marked. Same for enum variants:

mod config {
    pub struct Settings {
        pub port: u32,        // public field
        secret: String,       // private even though Settings is pub
    }

    impl Settings {
        pub fn new(port: u32) -> Self {
            Settings { port, secret: String::new() }
        }
    }
}

This is how you build clean APIs: expose the bare minimum (constructors, methods, sometimes a few fields), keep everything else private. Callers can't reach into your internals, so you're free to refactor them later.

Path syntax

Useful resources

Making something reachable

This step doesn't compile. The compiler isn't being awkward; it's enforcing the default that everything inside a module is private to that module.

Hit Run, read the error, and make the smallest change that fixes it. The error message names the exact item it's complaining about.

Exercise 1 of 3
Open in Web Editor

Results

    Compiler / runtime output
    
                
    Stuck? Show a hint No spoilers, just a nudge
    1. The compiler complains about a private item with a path like calculator::add. Look at where add is declared.
    2. Functions inside a module are private by default. Add pub in front of the fn keyword on the one the compiler is naming.
    3. mod calculator {
          pub fn add(a: i32, b: i32) -> i32 {
              a + b
          }
      }
      

    `pub` on enums (and their variants)

    Same idea as the previous step, but on an enum. Mark the type public and the compiler accepts the call site.

    One small bonus: enum variants inherit visibility from the enum itself. Once the enum is pub, the variants come along for the ride. Rust spares you the boilerplate, because an enum whose variants you can't name is useless to anyone outside the module.

    Compile, read the error, fix it.

    Exercise 2 of 3
    Open in Web Editor

    Results

      Compiler / runtime output
      
                  

      `pub` on structs (and their methods)

      You've now seen pub on a function and on an enum. Both followed the same rule: pub the item, done. Structs are where that rule breaks down.

      Marking a struct pub makes the type visible from outside its module. The fields and methods don't come along for the ride: each one stays private until you opt it in individually. That's a feature, not an inconvenience. It lets you expose the type while keeping the implementation behind a curtain.

      This step is broken in more than one place. Compile, read the error, fix the smallest thing, compile again, and repeat until the compiler runs out of complaints.

      Exercise 3 of 3
      Open in Web Editor

      Results

        Compiler / runtime output
        
                    
        Stuck? Show a hint No spoilers, just a nudge

        get_status

        1. The compiler is calling status::State private. That's the whole error.
        2. Make the enum itself pub. The variants come along for free (unlike struct fields).
        3. mod status {
              #[derive(Debug, PartialEq)]
              pub enum State {
                  Running,
                  Stopped,
              }
          }
          

        create_settings

        1. The first error is about the Settings type being private. pub it. Compile again. The next error is about Settings::new being private. pub it. Compile again. The next error is about get_port being private. You see where this is going.
        2. pub struct Settings does not make the fields or methods public. Each item gets its own pub.
        3. mod config {
              pub struct Settings {
                  port: u32,         // still private; not used from outside
              }
          
              impl Settings {
                  pub fn new(port: u32) -> Self {
                      Settings { port }
                  }
          
                  pub fn get_port(&self) -> u32 {
                      self.port
                  }
              }
          }
          
        Next chapter 21Parsing Structured Text and Generics