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 2
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.
    Reveal the full solution Spoiler: the complete answer
    // Module with a function.
    mod calculator {
        pub fn add(a: i32, b: i32) -> i32 {
            a + b
        }
    }
    
    /// Wraps `calculator::add` so callers don't have to know which
    /// module the addition lives in.
    fn calculate(x: i32, y: i32) -> i32 {
        calculator::add(x, y)
    }
    
    #[test]
    fn test_calculate() {
        assert_eq!(calculate(2, 3), 5);
    }
    

    Public type, private fields

    This is the real visibility lesson, and it catches almost everyone the first time.

    pub struct Settings makes the type visible outside its module. It does not make the fields public. port stays private until you mark it pub on its own, even though the struct around it is public.

    So a caller outside the module cannot write settings.port. That line fails to compile. The supported path is a pub accessor like get_port, and that is the whole reason the pattern exists: you expose a stable method and keep the field free to change later.

    The same opt-in rule covers methods. new and get_port are private until you pub each one, so this step is broken in more than one place. Compile, read the error, pub the item it names, and repeat until the compiler runs out of complaints.

    When you want something between fully public and fully private, pub(crate) makes an item visible everywhere in your own crate while keeping it hidden from outside users. That is the usual choice for helpers several modules share but that aren't part of your public API.

    Exercise 2 of 2
    Open in Web Editor

    Results

      Compiler / runtime output
      
                  
      Stuck? Show a hint No spoilers, just a nudge
      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. Leave port itself private. The test reaches it through get_port, which is the point: the field stays private even though the struct is public.
      Reveal the full solution Spoiler: the complete answer
      // Module with a struct and a couple of methods.
      mod config {
          pub struct Settings {
              port: u32,
          }
      
          impl Settings {
              pub fn new(port: u32) -> Self {
                  Settings { port }
              }
      
              pub fn get_port(&self) -> u32 {
                  self.port
              }
          }
      }
      
      /// Builds a `config::Settings` for callers that don't live inside
      /// the `config` module.
      fn create_settings() -> config::Settings {
          config::Settings::new(8080)
      }
      
      #[test]
      fn test_create_settings() {
          let settings = create_settings();
      
          // `settings.port` would not compile from out here: the field is
          // private even once `Settings` is `pub`. Go through the accessor.
          assert_eq!(settings.get_port(), 8080);
      }
      
      Next chapter 21Parsing structured text and generics