Skip to content
Learn Rust
Theme

Splitting code into files: mod and use

Two little keywords trip up many newcomers: mod and use. They look related, but they do completely different jobs. Once you see which is which, splitting a program into files is easy.

Remembermod says “this file is part of my program”, once, in the parent. use gives a short name to something, in any file that wants it.

This page is the step-by-step. The Modules lesson is the reference: every pub form, re-exports, globs and the prelude.

mod pricing; use crate::pricing::total;
What it means “There’s a module called pricing. Load it from pricing.rs.” “In this file, let me write total instead of crate::pricing::total.”
Where you write it Once, in the parent file (usually main.rs) In any file that wants the short name
Loads a file? Yes Never
Needed for crates and std? No Optional: it’s only a shortcut
Closest thing in Go Nothing: the compiler takes the .go files of a package folder import (of a whole package)
Closest thing in Python import pricing does both jobs at once. Rust splits them in two. from pricing import total
Closest thing in TypeScript Nothing as a statement: a file joins when something imports it import { total } from "./pricing", which also loads the file
Closest thing in Java, Kotlin, C# Nothing: the build gives the compiler its files import (Java, Kotlin), using (C#)
Closest thing in C, C++ Nothing in the code: the file is listed in the build using in C++; C has none (#include pastes a header)

This explains why you see use lines with no mod lines in most files. Most names in your code come from one of three places, and only one of them needs mod:

Where it comes from Example Needs mod?
The standard library use std::collections::HashMap; No. std is available by name in every file.
A crate in Cargo.toml use serde::Deserialize; No. Every normal dependency is available by name in every file (a hyphen in its name becomes _).
Your own files use crate::models::Order; Yes, once, in the parent file. Then every file can use it.

Here’s a program split across four files. The notes on the right show which files have mod lines:

Project layout
shop/
├── src/
│   ├── pricing/
│   │   └── discounts.rs        no mod lines
│   ├── main.rs                 mod models; mod pricing;
│   ├── models.rs               no mod lines
│   └── pricing.rs              mod discounts; use crate::models::Order;
├── Cargo.lock
└── Cargo.toml
Rust
mod models; // src/models.rs is part of this program
mod pricing; // so is src/pricing.rs, plus anything it declares
use models::Order; // a shortcut: now we can write Order instead of models::Order
fn main() {
let order = Order::new("ana", 3, 1250);
println!("{}", order.summary());
println!("total after discounts: {}", pricing::total(&order));
}
$ cargo run
3 x 1250 cents for ana
total after discounts: $33.75
  1. main.rs is where Rust starts. Its two mod lines say “models.rs and pricing.rs are part of this program”. Then use models::Order; lets it write Order instead of models::Order.
  2. models.rs declares nothing. It doesn’t need to: main.rs already declared it.
  3. pricing.rs has one mod line, for its own child discounts. Because the parent is pricing, Rust looks for that file in a folder named after it: src/pricing/discounts.rs. It also uses Order, with no mod models;, because main.rs already declared models. crate:: means “start from the top of this crate”, and here the crate is the program (crates and packages).
  4. discounts.rs declares nothing. Its parent, pricing.rs, declared it.

The mod lines build a tree of modules, starting at main.rs. The use lines just point into it:

The module tree
crate (src/main.rs)
├── models (src/models.rs)
└── pricing (src/pricing.rs)
└── discounts (src/pricing/discounts.rs)

To refer to something in this tree, start from the top with crate::, as in crate::models::Order, or one level up with super::, as in super::models::Order from inside pricing.

Why do some files have use lines but no mod lines?

Section titled “Why do some files have use lines but no mod lines?”

Because mod is written once, in the parent, and that’s usually main.rs (or lib.rs for a library). Every other file just uses what’s already been declared, plus things from std and your crates, which never need mod at all.

What if I write mod models; in pricing.rs too?

Section titled “What if I write mod models; in pricing.rs too?”

Then you’re declaring a second, different module called models, inside pricing. Rust looks for its file at src/pricing/models.rs, doesn’t find it, and stops. The error’s last line says what to do instead:

$ cargo build
error[E0583]: file not found for module `models`
 --> src/pricing.rs:2:1
  |
2 | mod models; // a second, different module: pricing::models
  | ^^^^^^^^^^^
  |
  = help: to create the module `models`, create file "src/pricing/models.rs" or "src/pricing/models/mod.rs"
  = note: if there is a `mod models` elsewhere in the crate already, import it with `use crate::...` instead

Does use make code slower, or load anything?

Section titled “Does use make code slower, or load anything?”

No. use is resolved while compiling and loads nothing at run time. It only chooses how you spell a name: you can write the full path instead, like models::Order::new(...). Try it #2 below does that and prints the same lines.

The older way to give a module a folder. src/pricing/mod.rs means the same as src/pricing.rs (Try it #3 moves the file to show it), and you’ll see both in real projects. The Book calls the pricing.rs style more idiomatic and the other “still supported”. Using both for one module is an error (layouts compared).

Sharing: items are private until you say pub

Section titled “Sharing: items are private until you say pub”
  • A function, struct, field or module is visible only inside its own module, and the modules inside that one, unless you mark it pub (private by default). Enum variants and trait methods are the exception: they take the visibility of their enum or trait (fields and variants).
  • That’s why Order’s fields are pub: pricing.rs reads order.quantity.
  • discounts isn’t pub in pricing.rs, so main.rs can’t reach it, even though bulk inside it is pub. A module is only as visible as the path to it.
  • pub(crate) means “visible anywhere in this crate, but not to other crates that use it as a library”. Try it #1 below shows the discounts rule.
  1. Reach into discounts from main.rs. Call pricing::discounts::bulk(...) there. The compiler says the module is private. Write pub mod discounts; in pricing.rs and it prints the discount, 375 cents:

    $ cargo build
    error[E0603]: module `discounts` is private
      --> src/main.rs:11:45
       |
    11 |     println!("discount alone: {}", pricing::discounts::bulk(order.quantity, gross));
       |                                             ^^^^^^^^^  ---- function `bulk` is not publicly re-exported
       |                                             |
       |                                             private module
       |
    note: the module `discounts` is defined here
      --> src/pricing.rs:1:1
       |
     1 | mod discounts; // src/pricing/discounts.rs: a module that lives inside pricing
       | ^^^^^^^^^^^^^^
    terminal
    $ cargo run
    3 x 1250 cents for ana
    total after discounts: $33.75
    discount alone: 375
    
  2. Delete the use line. Remove use models::Order; and write models::Order::new(...). The output is the same two lines:

    terminal
    $ cargo run
    3 x 1250 cents for ana
    total after discounts: $33.75
    
  3. Move pricing.rs into its folder. Rename src/pricing.rs to src/pricing/mod.rs, nothing else. Same two lines again:

    terminal
    $ mv src/pricing.rs src/pricing/mod.rs
    $ cargo run
    3 x 1250 cents for ana
    total after discounts: $33.75
    

How other languages decide what’s in your program

Section titled “How other languages decide what’s in your program”
Language A file joins your program when… Import Shared by default?
Rust its parent declares it: mod db; use crate::db::User; No: add pub
Go it sits in the package folder import "example.com/app/db" Other packages see only capitalised names
Python something imports it from app.db import User Yes; _name is private by convention only
TypeScript something imports it (or tsconfig.json includes it) import { User } from "./db" No: add export
Java the build compiles it (a source folder, or the files javac is given) import com.app.db.User; Package only: add public
Kotlin the build compiles it (a source folder, or the files kotlinc is given) import app.db.User Yes (public is the default); internal and private exist
C# it is in the project using App.Db; Types: project only (internal); members: private. Add public
C it is listed in the build #include "db.h" Yes (external linkage); static hides
C++ it is listed in the build #include "db.h" or import db; Yes; static or an unnamed namespace hides

Most rows above come from tiny projects run in the image. Open the log to see them:

The runs behind the table
terminal
$ ls   # Go: helper.go is never mentioned by main.go
db  go.mod  helper.go  main.go
$ go run .   # Go
helper.go is in the package, nothing declared it
db
$ go build ./...   # Go: main.go now calls db.secret()
# example.com/app
./main.go:11:17: name secret not exported by package db
$ python3 main.py   # Python: unused.py is beside it, never imported
db.py loaded
user still importable
$ tsc bad.ts   # TypeScript: db.ts has a Hidden without export
bad.ts(1,10): error TS2459: Module '"./db"' declares 'Hidden' locally, but it is not exported.
$ javac ... && java app.Main   # Java
user
$ javac app/Bad.java app/db/Hidden.java   # Java: Hidden has no public
app/Bad.java:2: error: Hidden is not public in app.db; cannot be accessed from outside package
import app.db.Hidden;
             ^
1 error
$ kotlinc Main.kt Helper.kt Priv.kt ... && java -jar   # Kotlin
Helper.kt joined without a declaration
2
$ kotlinc Bad.kt Priv.kt   # Kotlin: secret() is private
Bad.kt:2:18: error: cannot access 'fun secret(): Int': it is private in file.
import app.other.secret
                 ^^^^^^
Bad.kt:3:13: error: cannot access 'fun secret(): Int': it is private in file.
fun bad() = secret()
            ^^^^^^
$ dotnet run   # C#: Extra.cs is in the project, never mentioned
Extra.cs joined without a declaration
$ dotnet run   # C#: Holder.secret has no modifier, so it is private
/home/you/shop/cs/App2/Program.cs(1,32): error CS0122: 'Holder.secret' is inaccessible due to its protection level [/home/you/shop/cs/App2/Program.csproj]

The build failed. Fix the build errors and run again.
$ gcc main.c   # C: util.c is not in the build
/usr/bin/ld: /tmp/ccXXXX.o: in function `main':
main.c:(.text+0x8): undefined reference to `util'
collect2: error: ld returned 1 exit status
$ gcc main.c util.c && ./a.out   # C: util.c listed
42
$ gcc static_main.c static_hid.c   # C: hid() is static
/usr/bin/ld: /tmp/ccXXXX.o: in function `main':
static_main.c:(.text+0x8): undefined reference to `hid'
collect2: error: ld returned 1 exit status
$ g++ main.cpp   # C++: util.cpp is not in the build
/usr/bin/ld: /tmp/ccXXXX.o: in function `main':
main.cpp:(.text+0x8): undefined reference to `util()'
collect2: error: ld returned 1 exit status
$ g++ main.cpp util.cpp && ./a.out   # C++
42

Commands are shortened in the log (for example, tsc was run with --noEmit --target es2022 --module nodenext, and the javac and kotlinc lines are abbreviated). Java, Kotlin, C and C++ were given their file lists by hand, as a build tool would. The linker’s temporary file name is replaced by ccXXXX, because it is different on every run. C# ran from the image’s offline project template with dotnet run --no-restore: it shows that an unlisted Extra.cs is compiled, and that a member with no modifier is private. That a type with no modifier is internal was checked in a separate two-project run that is not in this log. Not run here: C++ import db;, C++ unnamed namespaces, and Python’s _name convention.

Gotcha

Creating a file and expecting it to just be there

Looks right at a glance, but Rust won't compile it.

No direct equivalent in Go. Just a Rust rule.No direct equivalent in Kotlin. Just a Rust rule.No direct equivalent in Python. Just a Rust rule.No direct equivalent in TypeScript. Just a Rust rule.Not run in Java on this page.Not run in C# on this page.C refuses this too. Same idea, different words.C++ refuses this too. Same idea, different words.
Rustsrc/main.rs✗ won't compile
// src/main.rs, without `mod pricing;`
mod models;
use models::Order;
fn main() {
let order = Order::new("ana", 3, 1250);
println!("{}", order.summary());
println!("total after discounts: {}", pricing::total(&order));
}
Gono equivalent

In Go, the .go files in the package folder are included automatically (not _test.go files, files whose names start with _ or ., or files excluded by build constraints). There's nothing to declare.

Kotlinno equivalent

In Kotlin the build gives the compiler a set of files, and files in the same package see each other without any declaration.

Pythonno equivalent

A Python file runs when something imports it, and not otherwise. Nothing declares it ahead of time, but nothing loads it by being in the folder either.

TypeScriptno equivalent

A TypeScript file is compiled when something imports it (or when tsconfig.json includes it). The import is the declaration.

There is no Java version of this one.

There is no C# version of this one.

#include <stdio.h>
int util(void);
int main(void) {
printf("%d\n", util());
return 0;
}
$ gcc -Wall -Wextra main.c -o main && ./main✗ refuses too
/usr/bin/ld: /tmp/ccXXXX.o: in function `main':
main.c:(.text+0x8): undefined reference to `util'
collect2: error: ld returned 1 exit status
#include <iostream>
int util();
int main() {
std::cout << util() << "\n";
return 0;
}
$ g++ -std=c++20 -Wall -Wextra main.cpp -o main && ./main✗ refuses too
/usr/bin/ld: /tmp/ccXXXX.o: in function `main':
main.cpp:(.text+0x8): undefined reference to `util()'
collect2: error: ld returned 1 exit status
What does the compiler say? Guess first, then open.
$ cargo buildWhat is E0433?
error[E0433]: cannot find module or crate `pricing` in this scope
 --> src/main.rs:9:43
  |
9 |     println!("total after discounts: {}", pricing::total(&order));
  |                                           ^^^^^^^ use of unresolved module or unlinked crate `pricing`
  |
help: to make use of source file src/pricing.rs, use `mod pricing` in this file to declare the module
  |
2 + mod pricing;
What happens in Go

In Go, the .go files in the package folder are included automatically (not _test.go files, files whose names start with _ or ., or files excluded by build constraints). There's nothing to declare.

What happens in Kotlin

In Kotlin the build gives the compiler a set of files, and files in the same package see each other without any declaration.

What happens in Python

A Python file runs when something imports it, and not otherwise. Nothing declares it ahead of time, but nothing loads it by being in the folder either.

What happens in TypeScript

A TypeScript file is compiled when something imports it (or when tsconfig.json includes it). The import is the declaration.

Why it looks fine

In Go, the .go files in a package folder are compiled together (test files and files excluded by build constraints aside). Build tools for Java, Kotlin and C# typically collect a whole source folder. In those languages, create the file and it's part of the build.

Why it looks fine

In Go, the .go files in a package folder are compiled together (test files and files excluded by build constraints aside). Build tools for Java, Kotlin and C# typically collect a whole source folder. In those languages, create the file and it's part of the build.

What happens in C

A C file that is not listed in the build is not compiled, and the linker reports an undefined reference to what is in it. Like mod, the build has to be told about the file.

What happens in C++

Same as C: a .cpp file that is not listed in the build is not compiled, and the linker complains about what is missing. Like mod, the build has to be told about the file.

What Rust sees

Rust starts at main.rs and only follows the mod lines it finds. src/pricing.rs exists, but nothing declared it, so it isn't part of the program.

Why Rust says no

Being explicit means the module tree is written in your code, not decided by whatever files happen to be in a folder. The fix is one line, and the compiler's help: shows exactly which one.

The fix

Compiles and runs.

Rust, fixedsrc/main.rsRun ↗
mod models; // src/models.rs is part of this program
mod pricing; // so is src/pricing.rs, plus anything it declares
use models::Order; // a shortcut: now we can write Order instead of models::Order
fn main() {
let order = Order::new("ana", 3, 1250);
println!("{}", order.summary());
println!("total after discounts: {}", pricing::total(&order));
}
$ cargo run
3 x 1250 cents for ana
total after discounts: $33.75