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.
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.
crate::foo::bar is the absolute path from the crate root.super::bar goes up one module (like .. in filesystem paths).self::bar is the current module (rarely needed).use foo::bar; brings bar into scope so you can call it without the
full path.pub(crate)
is a useful middle ground: visible everywhere in your crate, hidden
from external users.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.
calculator::add. Look at where add is declared.pub in
front of the fn keyword on the one the compiler is naming.mod calculator {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
}
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.
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.
status::State private. That's the
whole error.pub. The variants come along for free
(unlike struct fields).mod status {
#[derive(Debug, PartialEq)]
pub enum State {
Running,
Stopped,
}
}
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.pub struct Settings does not make the fields or methods
public. Each item gets its own pub.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
}
}
}