TL;DR: Modules are Rust's namespace and visibility system. In 2026, the real question is not just what
modmeans, but how to structure a crate so the public API stays clean while internal code stays organized. Use modules to separate concerns,pubto expose only what should be visible, andpub useto shape a cleaner API surface.
What Are Modules in Rust?
Modules are Rust's namespace and visibility system, and they are the main tool for deciding what code stays internal versus what becomes part of a crate's public API.
// Everything private by default
mod math {
pub fn add(a: i32, b: i32) -> i32 { a + b } // public
fn helper() -> i32 { 42 } // private to this module
}
fn main() {
let result = math::add(2, 3); // OK; pub
// math::helper(); // COMPILE ERROR; private
}The double-colon :: is the path separator. math::add means "the add function in the math module."
This page matters most for Rust learners moving beyond single-file examples and for engineers trying to keep larger crates readable instead of dumping everything into main.rs or lib.rs.
How Do Modules Map to Files?
A module can be inline (defined with { }) or declared as a separate file. The file path mirrors the module path.
src/
├── main.rs ← crate root; `mod network;` declares the module
├── network.rs ← defines the `network` module
└── network/
├── mod.rs ← alternative: defines `network` as a directory module
├── client.rs ← `mod client;` inside network/mod.rs or network.rs
└── server.rs ← `mod server;` inside network/mod.rs or network.rs// src/main.rs
mod network; // tells Rust to look for src/network.rs or src/network/mod.rs
fn main() {
network::client::connect();
}
// src/network.rs (or src/network/mod.rs)
pub mod client;
pub mod server;
// src/network/client.rs
pub fn connect() {
println!("connecting...");
}How Does Visibility Work?
Visibility modifiers control which modules can see an item, and the most practical distinction is usually between pub, pub(crate), and private-by-default code.
mod outer {
pub fn public_fn() {} // visible everywhere
pub(crate) fn crate_fn() {} // visible within this crate only
pub(super) fn parent_fn() {} // visible in the parent module
fn private_fn() {} // visible only within `outer`
mod inner {
pub fn inner_public() {} // pub, but inner is private; effectively private
pub(super) fn to_outer() {} // visible in `outer`
pub fn can_see_parent() {
super::private_fn(); // inner can see parent's private items
}
}
}A useful rule is that pub(crate) is often better than pub for internal types because it exposes them within the crate without turning them into stable public API by accident.
How Does use Work?
use imports a path into the current scope, which reduces repetition without changing the underlying visibility rules.
use std::collections::HashMap;
use std::io::{self, Read, Write}; // import multiple from same path
use std::fmt;
fn main() {
let mut map = HashMap::new(); // no need for std::collections::HashMap::new()
map.insert("key", "value");
}
// Rename on import
use std::collections::HashMap as Map;
// Re-export; makes an item part of your module's public API
pub use crate::network::client::connect; // callers can use your_crate::connectpub use is the re-export pattern, and it lets you create a clean public API surface even when your internal module structure is deeply nested.
Modules vs Crates and Packages in 2026
Choose modules to organize code inside one crate in 2026; choose multiple crates only when the boundaries are strong enough to justify separate compilation units and dependency surfaces.
| Module | Crate | Package | |
|---|---|---|---|
| Scope | Internal code organization | Compilation unit | Cargo project container |
| Separate dependencies | No | Yes | Contains one or more crates |
| Best fit | Group related code in one project | Split libraries or binaries cleanly | Publish or manage a project |
| Common mistake | Over-nesting trivial files | Splitting too early | Treating all three terms as interchangeable |
If your code is still one cohesive project, start with modules. Reach for multiple crates only when the separation gives you real compile-time, API, or ownership benefits.
How Do You Structure a Real Rust Project?
A typical Rust library or application separates concerns by module, with a flat public API re-exported from lib.rs.
my_app/
├── Cargo.toml
└── src/
├── lib.rs ← public API surface, re-exports
├── main.rs ← entry point (for binaries)
├── domain/
│ ├── mod.rs
│ ├── user.rs
│ └── order.rs
├── db/
│ ├── mod.rs
│ └── queries.rs
└── api/
├── mod.rs
├── routes.rs
└── middleware.rs// src/lib.rs
pub mod domain;
pub mod api;
mod db; // internal; not part of public API
pub use domain::user::User; // re-export for convenience
pub use domain::order::Order;This is where modules stop being a syntax topic and become an architecture topic.
Why Do Modules Matter Professionally?
Modules matter because real Rust codebases become hard to maintain fast when the public API, internal boundaries, and file layout all drift out of sync.
Teams that understand modules well usually produce crates that are easier to navigate, test, and reuse. Teams that do not often end up with giant roots, accidental public APIs, and confusing path imports everywhere. This is one of the simplest concepts in Rust syntax and one of the most important in day-to-day codebase quality.
Frequently Asked Questions
mod declares a module and creates a namespace in the module tree. use imports a path into the current scope as a shortcut. Think of mod as "create" and use as "bring into scope."
super refers to the parent module. crate refers to the crate root. self refers to the current module. These keywords let you navigate the module tree explicitly.
Within the same crate, yes, modules in the same crate can reference each other freely. Across crates, Cargo prevents circular dependencies at the crate level.
A conditional compilation block. The tests module is only compiled when running cargo test, which is why it is the standard unit-test pattern in Rust source files.
Sources
- The Rust Book ; Managing Growing Projects with Packages, Crates, and Modules
- Rust Reference ; Modules
Related Glossary Terms
- Cargo: Cargo manages crates; modules organize code within a crate
- Trait: Traits must be in scope (
use) to call their methods - Struct: Struct fields are private by default;
pubexposes them - Generic: Generics and visibility work together in module APIs
- Workspace: Modules organize code inside one crate; workspaces organize code across many crates
