pub Visibility
pub Visibility
Level 7 — Modules, Visibility & Project Structure Makes items public; items are private by default.
1. Prerequisites
modDeclaration — The boundaries thatpubactually allows you to cross.- fn — The most common items you will make public.
- Struct — The data structures with tricky field-level privacy rules.
2. Term Category
Rust-specific (the privacy shield): In Rust, absolute privacy is the default. Every function, struct, field, and module you create is strictly hidden from the outside world unless you explicitly place the pub keyword in front of it.
3. Explanation
(1) Design Motivation — "Why did we design this?"
In some languages (like Python), everything is public by default. If you write an internal helper function, you just have to add an underscore (_helper_function()) and hope no other developer decides to use it.
The Rust designers believed that exposing internal logic is incredibly dangerous. If someone else relies on your internal helper function, you can never change or delete that function without breaking their code!
Therefore, Rust makes everything completely private. You are forced to look at every single function and explicitly decide: "Is this part of my official public API?" If it is, you mark it with pub. This guarantees you never accidentally expose fragile internal logic.
(2) Reality Metaphor
Imagine a Restaurant.
The dining room is pub. Customers are allowed to sit there, look at the menu, and order food.
The kitchen is private. Customers are strictly forbidden from walking into the kitchen and touching the stoves. If the restaurant owner decides to upgrade from gas stoves to electric stoves (refactoring internal logic), the customers don't care, because they never had access to the kitchen anyway.
If the kitchen stoves were pub, changing them would break the customers' workflow! By keeping internal things private, you give yourself the freedom to upgrade your code later without breaking anyone else's code.
(3) Rust Code Examples
Short Snippet (Function Privacy)
Functions inside a module cannot be seen from the outside unless they have the pub keyword.
mod restaurant {
// This is public! Anyone can call this.
pub fn order_food() {
cook_steak(); // It can call private functions inside its own module!
}
// This is private! Only functions inside `restaurant` can call this.
fn cook_steak() {
println!("Sizzle...");
}
}
fn main() {
// SUCCESS! We are allowed to order food.
restaurant::order_food();
// ERROR! "function `cook_steak` is private"
// restaurant::cook_steak();
}
Fuller Example (Struct Field Privacy)
Structs have a very strict privacy rule. Even if you make the struct itself pub, all of its fields remain strictly private! You must make the fields pub individually.
mod banking {
// The struct is public, so people can use the `BankAccount` type.
pub struct BankAccount {
// The name is public, anyone can read or change it.
pub owner_name: String,
// The balance is PRIVATE. No one outside the `banking` module
// is allowed to look at or change this number!
balance: i32,
}
impl BankAccount {
// We provide a public method to let people *read* the balance safely.
pub fn get_balance(&self) -> i32 {
self.balance
}
}
}
fn main() {
// We cannot create a BankAccount directly here because `balance` is private!
// The compiler strictly forbids us from bypassing the privacy rules.
}
4. Common Mistakes & Pitfalls
Mistake 1: Assuming Struct Fields Become Public Automatically when Declaring pub struct
The mistake: Marking a struct public via pub struct User { name: String } and expecting callers outside the module to access user.name.
Why it is wrong: In Rust, declaring pub struct User makes the type name public, but all struct fields remain private by default. Callers outside the module cannot access or initialize name unless the field is explicitly marked pub name: String.
Incorrect:
pub struct User {
name: String, // ❌ Field is private! Cannot be accessed outside this module!
}
Fix:
pub struct User {
pub name: String, // Explicitly marked public!
}
Mistake 2: Assuming Enum Variants Require Individual pub Annotations
The mistake: Trying to write pub enum Status { pub Active, pub Inactive }.
Why it is wrong: Unlike struct fields, making an enum public automatically makes all of its variants and variant fields public. Writing pub on individual enum variants is invalid syntax.
Incorrect:
pub enum Status {
pub Active, // ❌ Syntax error! Enum variants inherit public visibility automatically
}
Fix:
pub enum Status {
Active, // Correct!
}
Mistake 3: Forgetting Public Parent Module Declarations for Public Child Items
The mistake: Declaring pub fn helper() inside mod foo, but declaring mod foo; (without pub) in lib.rs.
Why it is wrong: Even if an item is pub, it cannot be accessed from outside the parent module if the parent module itself is private. The module must be pub mod foo; to be reachable by external callers.
5. Practice Exercises
Exercise 1: The Secret Age
Scenario: The following code fails to compile with the error: field 'age' of struct 'Employee' is private. Fix the code by adding a single word to the struct definition.
mod hr_department {
pub struct Employee {
pub name: String,
age: u8, // TODO: Fix this line!
}
}
fn main() {
let emp = hr_department::Employee {
name: String::from("Alice"),
age: 30, // ERROR! We are not allowed to set this!
};
println!("{} is {} years old.", emp.name, emp.age);
}
Answer
Exercise 2: Selective Field Visibility in Structs
Scenario: Define pub struct User { pub name: String, age: u32 } where age remains private.
Expected output:
Answer
Name: Alice
Implementation
pub struct User {
pub name: String,
age: u32,
}
impl User {
pub fn new(name: String, age: u32) -> Self { Self { name, age } }
}
fn main() {
let u = User::new("Alice".into(), 30);
println!("Name: {}", u.name);
}
Technical Explanation
Struct fields can have granular visibility independent of container struct visibility.
Exercise 3: Restricting Visibility to Ancestors with pub(in path)
Scenario: Use pub(in crate::outer) to restrict a function's visibility to an ancestor module.
Expected output:
Answer
Restricted ancestor function called
Implementation
mod outer {
pub mod inner {
pub(in crate::outer) fn secret() { println!("Restricted ancestor function called"); }
}
pub fn call() { inner::secret(); }
}
fn main() { outer::call(); }
Technical Explanation
pub(in path) restricts visibility to designated ancestor module paths.
6. Related Terms
modDeclaration — The boundaries thatpubactually allows you to cross.pub(crate)/pub(super)— Advanced, fine-grained versions ofpubthat allow you to restrict visibility to specific areas instead of making things completely public.- Crate — Related concept: Crate.
- Re-exporting (
pub use) — Related concept: Re-exporting (pub use). cargo doc— Related concept:cargo doc.
7. Key Takeaways
- In Rust, everything (functions, structs, traits, modules) is strictly private by default.
- Add the
pubkeyword to expose an item to parent modules or external users. - Making a struct
pubdoes not make its fieldspub. Fields must be explicitly markedpubindividually. - Making an enum
pubdoes make all of its variantspubautomatically.