When One File Is No Longer Enough
\n\nYou are writing in Rust. Your project is growing. Just yesterday, main.rs held everything — functions, structs, a couple of configs. And today you spend five minutes trying to find a specific function among a thousand lines. Sound familiar?
Sooner or later, every Rust developer faces the need to split code into logical parts. It seems simple enough, right? Create a folder, arrange the files, import them — and you're done. But the Rust module system is known for its strictness. One wrong entry in mod.rs — and the compiler throws an error that will take you half an hour to figure out.
In this article, we will break down the top errors when splitting a Rust module into multiple files and, most importantly, show you how to avoid them. You will learn how to properly organize your file structure, when to use mod.rs and when to use lib.rs, and why pub use is your best friend.
Anatomy of the Rust Module System
\n\nBefore diving into the errors, let's recall the basics. In Rust, a module is a unit of code organization. By default, each file is a module, but it's not that straightforward.
\n\nThere are two approaches:
\n\n- \n
- File-as-module: The file
utils.rsautomatically becomes theutilsmodule if you declaremod utils;inlib.rsormain.rs. \n - Directory-as-module: A folder
utils/with amod.rsfile inside. In this case,mod.rsis the entry point to the module. \n
Sounds simple, but this is where most of the pitfalls lie. Let's break down the most common ones.
\n\nTop 5 Errors When Splitting a Module into Multiple Files
\n\nError #1: Forgot to Declare a Submodule
\n\nYou created the file network/tcp.rs and are trying to use it. You write use network::tcp; — and get a compilation error. Why? Because Rust does not scan folders automatically. You must explicitly declare each submodule.
// network/mod.rs\n// ❌ Error: submodule tcp is not declared\n// use network::tcp; // will not compile\n\n// ✅ Correct:\npub mod tcp; // declare the submodule\n\n\nHow to avoid: Always check that you have added pub mod submodule_name; in the parent module. This is like an explicit import — Rust requires clarity.
Error #2: Confusion Between mod.rs and lib.rs
\n\nBeginners often place mod.rs in the project root, next to main.rs. This works, but only if you are creating a library. For an executable file (main.rs), the root module is main.rs itself. And mod.rs is a file inside a folder.
my_project/\n├── main.rs // root module for the binary\n├── lib.rs // root module for the library (if any)\n├── utils/\n│ ├── mod.rs // entry point for the utils module\n│ └── helpers.rs\n\n\nHow to avoid: Remember the rule: mod.rs lives only inside a module folder. If you need a root module for a library — use lib.rs.
Error #3: Forgot pub in Submodules
\n\nYou declared a submodule, but still cannot call a function. Most likely, you forgot to specify pub.
// network/mod.rs\npub mod tcp;\n\n// network/tcp.rs\n// ❌ Function is private by default\nfn connect() { /* ... */ }\n\n// ✅ Make it public\npub fn connect() { /* ... */ }\n\n\nIn Rust, everything is private by default. This is not a bug, but a feature: you decide what to expose to the outside world. If a function is needed outside the module — add pub.
How to avoid: Use pub intentionally. For internal utilities, keep them private. For an API, make them public.