← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Rust Module Visibility

Rust Intermediate
debt(d7/e5/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). Over-exposing items with bare `pub` compiles fine — the compiler never errors on something being too public. Clippy has some lints (e.g. redundant_pub_crate) but the core risk of committing internal helpers to a stable public API is silent unless caught in code review or API-diff tooling. The code_pattern regex flags pub struct with pub fields but most accidental over-exposure passes unnoticed.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix (default to private, use pub(crate), keep fields private behind accessors) is simple in isolation, but once a `pub` item has leaked into a published API, tightening visibility is a breaking change that ripples through all call sites referencing it and may require adding constructors/accessors across the module.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). applies_to spans library/web/cli/queue-worker contexts, and visibility choices form the public API contract; over-exposed items create a maintenance obligation across versions that shapes future refactors. It slows many work streams without fully defining system shape.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception is explicit: developers assume marking a struct `pub` makes all fields public, when each field needs its own `pub`. This contradicts the mental model from many other languages where public class implies accessible members, and the path-visibility rule (every module in the path must be pub) is a further documented gotcha.

About DEBT scoring →

Also Known As

rust pub keyword pub(crate) rust module privacy rust visibility modifiers

TL;DR

Rust items are private to their module by default; pub and its scoped variants control exactly which other modules can reach them.

Explanation

Rust organizes code into a tree of modules, and every item - functions, structs, enums, fields, constants, modules themselves - has a visibility that decides who may name it. The default is private, meaning an item is visible only within the module that defines it and that module's descendants. This is the opposite of many languages where everything is public unless hidden, and it is the foundation of Rust's encapsulation: nothing leaks out of a module until you deliberately export it.

The `pub` keyword makes an item visible to outside code, but visibility is also subject to the path of re-exports along the way. An item must be `pub` and every module in its path must also be `pub` for an external crate to reach it. For finer control Rust offers restricted forms. `pub(crate)` exposes an item to the whole current crate but never beyond it, which is the right choice for internal helpers shared across modules that should not become part of your public API. `pub(super)` limits visibility to the immediate parent module. `pub(in path)` restricts visibility to a specific ancestor module by path. A bare `pub(self)` is equivalent to private.

Struct fields deserve special attention. Making a struct `pub` does not make its fields public; each field has its own visibility. A struct with private fields can only be constructed inside its module, which lets you enforce invariants through constructor functions and prevents callers from depending on internal layout. This is how Rust types guarantee they are always in a valid state.

A common discipline is to keep the public surface minimal. Mark internal-only items `pub(crate)` rather than `pub` so they cannot become accidental API commitments that you must support across versions. Use `pub use` re-exports to present a clean, flat public path while keeping your internal module structure private. Getting visibility right means your crate's API is exactly what you intend, the compiler enforces encapsulation, and refactoring internal modules never breaks downstream users.

Common Misconception

Marking a struct `pub` makes all its fields public too. In reality each field has its own visibility and stays private unless individually marked `pub`, so a `pub struct` can still hide its internals.

Why It Matters

Over-exposing items with bare `pub` turns internal helpers into a public API contract you must maintain across versions, while correct scoping lets you refactor freely and enforce type invariants.

Common Mistakes

  • Marking helpers `pub` when `pub(crate)` is enough, accidentally committing internal functions to your stable public API.
  • Assuming a `pub struct` exposes its fields, when each field needs its own `pub` to be accessible.
  • Forgetting that an item only reaches an external crate if every module in its path is also `pub`.
  • Making struct fields public and losing the ability to enforce invariants through controlled constructors.
  • Using `pub(super)` or `pub(in path)` without understanding the module tree, exposing items to the wrong scope.

Avoid When

  • Building a binary crate with no external consumers, where the public/private distinction has little practical impact.
  • Rapid prototyping where locking down visibility slows iteration and there is no API to protect yet.
  • A field genuinely is plain data with no invariant, where a public field is simpler than boilerplate accessors.

When To Use

  • Designing a library crate where the public API must stay minimal and stable across versions.
  • Enforcing struct invariants by keeping fields private and exposing controlled constructors and methods.
  • Sharing helpers across modules of one crate without exposing them externally, using `pub(crate)`.
  • Re-exporting a clean public path with `pub use` while keeping the internal module layout private.

Code Examples

💡 Note
A good example shows a struct with private fields and a public constructor, while a bad example exposes all fields as public, breaking encapsulation and making it unsafe to change the internal representation later.
✗ Vulnerable
// lib.rs
pub mod store {
    // Everything is pub, so callers depend on internals.
    pub struct Account {
        pub balance: i64, // public field: no invariant enforcement
    }

    // Internal helper leaked into the public API.
    pub fn audit_log(msg: &str) {
        println!("audit: {}", msg);
    }
}

fn main() {
    // Callers can mutate balance directly into an invalid state.
    let mut a = store::Account { balance: 100 };
    a.balance = -500; // overdraft, no check possible
    store::audit_log("tampered");
    println!("{}", a.balance);
}
✓ Fixed
// lib.rs
pub mod api {
    // Private helper - only this module
    fn validate_input(x: i32) -> bool {
        x > 0
    }

    // Public struct with public field
    pub struct Request {
        pub id: u64,
        count: i32,  // Private field - use getter
    }

    impl Request {
        pub fn new(id: u64, count: i32) -> Self {
            Request { id, count }
        }

        // Public getter for private field
        pub fn count(&self) -> i32 {
            self.count
        }
    }

    // Shared across crate, not part of public API
    pub(crate) fn internal_helper() {}
}

pub mod utils {
    // Only visible in parent module
    pub(super) fn sibling_only() {}
}

// Private module - not exposed outside crate
mod private {
    pub fn hidden() {}  // Still private to external crates
}

Added 16 Jun 2026
Views 125
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 3 pings S 5 pings M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 2 pings M 1 ping T 1 ping W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T
No pings yet today
SEMrush 1
ChatGPT 12 PetalBot 8 SEMrush 8 Ahrefs 5 Brave Search 5 Amazonbot 5 Google 4 Perplexity 3 Applebot 3 Twitter/X 2 Bing 2 Unknown AI 1
crawler 58
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
Cargo rust Cargo is Rust's official build tool and package manager that compiles your code, downloads dependencies, and runs tests—all with simple commands.

Cargo is how you'll interact with Rust daily—building, testing, and managing dependencies. Mastering its commands makes you productive immediately and scales seamlessly to complex projects.

💡 When in doubt, `cargo check` compiles without producing a binary—it's faster and catches errors quickly.

Ask Codex about Cargo →
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Low
⚡ Quick Fix
Default to private; mark internal-only items `pub(crate)` and keep struct fields private behind constructor and accessor methods.
📦 Applies To
library web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
pub\s+struct\s+\w+\s*\{[^}]*pub\s+\w+\s*:
Auto-detectable: ✓ Yes
🤖 AI Agent
Confidence: Medium False Positives: High ✗ Manual fix Fix: Medium Context: File Tests: Update


✓ schema.org compliant