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.
// 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);
}
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.
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.port itself private.
The test reaches it through get_port, which is the point: the field stays private even though the struct is public.
// 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);
}