Frontend Labs
⋯

12. Modules, crates, and visibility

myapp/
├── Cargo.toml
└── src/
    ├── main.rs          // crate root of the binary
    ├── lib.rs           // crate root of the library (a package can have both)
    ├── config.rs        // mod config;
    └── net/
        ├── mod.rs       // or net.rs next to a net/ directory
        └── frame.rs     // mod frame; inside net
mod net {
    pub mod frame {
        pub struct Frame { pub len: u32, checksum: u32 } // `checksum` is private to this module

        impl Frame {
            pub fn new(len: u32) -> Self { Self { len, checksum: len ^ 0xFFFF } }
            pub fn valid(&self) -> bool { self.checksum == self.len ^ 0xFFFF }
        }

        pub(crate) fn internal_helper() -> u8 { 7 } // visible anywhere in THIS crate only
    }
    pub use frame::Frame; // re-export: callers write net::Frame
}

use net::Frame;

fn main() {
    let f = Frame::new(42);
    // let bad = net::frame::Frame { len: 1, checksum: 0 }; // error[E0451]: field `checksum` is private
    println!("{} {} {}", f.len, f.valid(), net::frame::internal_helper());
}
  • Everything is private by default. Use pub, pub(crate), or pub(super) to widen visibility. Go uses capitalization for the same purpose (Go §11.1); C uses static (C §13.1).
  • A private field means outside code cannot construct the struct with a literal. It has to go through your constructor, which can enforce invariants. This is how String guarantees valid UTF-8.
  • Features ([features] in Cargo.toml) are compile-time flags, like C's #ifdef, applied per crate: #[cfg(feature = "tls")].

Generated from real-life-rust-guide.md · 8 Oct 2026 16:36