Question
I want to organize a Rust module so that it contains multiple structs, with each struct defined in its own file. For example:
Math/
vector.rs
matrix.rs
complex.rs
I want these types to belong to the same overall module and be usable from main.rs like this:
use math::Vector;
fn main() {
// ...
}
However, Rust's module system is confusing to me, and it is not obvious how to split one module across multiple files. It seems like a module must live entirely in one file.
Is splitting a module this way considered idiomatic Rust? If so, how should it be structured?
Short Answer
By the end of this page, you will understand how Rust modules map to files and folders, how to split a module into submodules across multiple files, and how to expose types cleanly with pub use so they can be imported from a single module path.
Concept
Rust modules are primarily about organizing code and controlling visibility, not just about matching folders on disk.
A common beginner misunderstanding is thinking that one module must be written in one file. In Rust, a module can be declared in one place and its contents can be loaded from other files. This is the normal way to build larger projects.
The key ideas are:
mod name;declares a module and tells Rust to load it from another file.pub mod name;does the same thing, but makes that module public.pub use ...;re-exports items so users can access them from a simpler path.- Files and folders help Rust find module code, but the module tree is what really matters.
For your math example, each file like vector.rs, matrix.rs, and complex.rs is usually its own submodule inside a parent math module.
That means the real structure is not “one module split invisibly across files,” but rather:
- a parent module:
math - child modules:
math::vector,math::matrix,math::complex
Then, if you want , you can from the parent module.
Mental Model
Think of a Rust module like a folder with a front desk.
- The folder contains smaller rooms:
vector,matrix,complex - Each room has its own file
- The front desk is the parent
mathmodule - The front desk can either:
- tell visitors to go into a room directly:
math::vector::Vector - hand visitors the item directly:
math::Vectorusingpub use
- tell visitors to go into a room directly:
So the parent module is not forced to contain all the code itself. It can simply coordinate and expose things from its child modules.
Syntax and Examples
The most common way to structure this in Rust is:
src/
main.rs
math/
mod.rs
vector.rs
matrix.rs
complex.rs
main.rs
mod math;
use math::Vector;
fn main() {
let v = Vector { x: 3.0, y: 4.0 };
println!("({}, {})", v.x, v.y);
}
math/mod.rs
pub mod vector;
pub mod matrix;
pub mod complex;
pub use vector::Vector;
pub use matrix::Matrix;
pub use complex::Complex;
math/vector.rs
pub struct Vector {
x: ,
y: ,
}
Step by Step Execution
Consider this example:
// main.rs
mod math;
use math::Vector;
fn main() {
let v = Vector { x: 1.0, y: 2.0 };
println!("{} {}", v.x, v.y);
}
// math.rs or math/mod.rs
pub mod vector;
pub use vector::Vector;
// math/vector.rs
pub struct Vector {
pub x: f64,
pub y: f64,
}
Step by step
- Rust starts compiling
main.rs. - It sees
mod math;. - Rust looks for the
mathmodule in either:src/math.rs, orsrc/math/mod.rs
Real World Use Cases
Splitting modules across files is extremely common in real Rust projects.
1. Domain models
A project may group related types together:
user/
profile.rs
settings.rs
permissions.rs
This keeps each type focused and easier to maintain.
2. API clients
An HTTP client crate might organize code by API area:
api/
users.rs
payments.rs
invoices.rs
The parent module can re-export the most important public types.
3. Math or graphics libraries
Types like vectors, matrices, colors, transforms, and quaternions are often split into separate files but grouped under one module tree.
4. Compiler or parser code
Larger tools often have modules such as:
ast/
expr.rs
stmt.rs
types.rs
Each file handles one part of the language model.
5. Internal application organization
Even non-library applications use modules to keep code readable, testable, and easier to navigate.
Real Codebase Usage
In real projects, developers usually combine submodules with carefully chosen public exports.
Common pattern: internal structure, simple public API
pub mod vector;
pub mod matrix;
pub use vector::Vector;
pub use matrix::Matrix;
This gives you:
- internal organization:
math::vector,math::matrix - simple external usage:
math::Vector,math::Matrix
Pattern: hide implementation details
Sometimes the child modules are not made public:
mod vector;
mod matrix;
pub use vector::Vector;
pub use matrix::Matrix;
Now users can access math::Vector, but not math::vector::... directly. This is useful when you want a cleaner API surface.
Pattern: grouped responsibilities
Common Mistakes
1. Thinking multiple files automatically become one module
Each file is usually a separate module unless declared otherwise.
Broken expectation:
math/
vector.rs
matrix.rs
Rust does not automatically combine those into one math module by name alone. You still need a parent module file.
2. Forgetting to declare child modules
If math.rs does not declare mod vector;, Rust will not load vector.rs.
Broken example:
// math.rs
pub use vector::Vector;
This fails because vector was never declared.
Correct version:
mod vector;
pub use vector::Vector;
3. Forgetting pub
A type may exist but still be inaccessible.
Broken example:
Comparisons
| Concept | Purpose | Example | When to use |
|---|---|---|---|
mod | Declare a module | mod math; | When telling Rust where module code lives |
use | Bring a name into scope | use math::Vector; | When you want shorter names in the current file |
pub mod | Declare a public child module | pub mod vector; | When external code should access the child module path |
pub use | Re-export an item | pub use vector::Vector; |
Cheat Sheet
// main.rs
mod math;
use math::Vector;
// math.rs or math/mod.rs
mod vector;
pub use vector::Vector;
// math/vector.rs
pub struct Vector {
pub x: f64,
pub y: f64,
}
Rules to remember
mod name;tells Rust to load a module from a file.- A parent module must declare its child modules.
usedoes not create modules; it only imports names.pubcontrols visibility.pub usere-exports names from one module level to another.- Files are usually lowercase:
vector.rs, notVector.rs.
Common layouts
src/
main.rs
math.rs
math/
vector.rs
FAQ
Can a Rust module be split across multiple files?
Yes. This is a normal and idiomatic way to organize Rust code. Usually, a parent module declares child modules stored in separate files.
Why doesn't Rust automatically combine files in a folder into one module?
Rust requires explicit module declarations so the module tree is clear and visibility is controlled intentionally.
Should I use mod.rs or math.rs?
Both are valid. Many newer codebases prefer math.rs plus a math/ folder, but mod.rs is still completely acceptable.
How do I make use math::Vector; work?
Re-export the type from the parent module:
pub use vector::Vector;
What is the difference between mod and use in Rust?
mod declares a module. use imports a name from an existing module path into the current scope.
Do I need pub mod vector; or just mod vector;?
Mini Project
Description
Build a small Rust math module that stores several numeric types in separate files while exposing a clean API from one parent module. This demonstrates how Rust modules, child modules, and re-exports work together in a practical project structure.
Goal
Create a math module with separate files for Vector, Matrix, and Complex, then use those types from main.rs through a clean import path.
Requirements
- Create a parent
mathmodule that loads child modules from separate files. - Define one public struct in each child module:
Vector,Matrix, andComplex. - Re-export the structs so they can be imported directly from
math. - In
main.rs, create at least one value of each type and print something from it.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Can a Struct Extend Another Struct in Rust? Composition vs Inheritance
Learn how Rust handles struct reuse without inheritance, using composition, traits, and wrapper structs with practical examples.