Question

How to Import Modules from Another File in Rust Cargo Projects

rustmoduleprogram-entry-pointrust-cargo

Question

In a Rust Cargo project, how do you move a module into a separate file and use it from main.rs?

For example, this works when everything is in one file:

fn main() {
    hello::print_hello();
}

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

But after splitting the code into two files like this:

src/
├── hello.rs
└── main.rs

src/main.rs

use hello;

fn main() {
    hello::print_hello();
}

src/hello.rs

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

running cargo build gives this error:

error[E0432]: unresolved import `hello`

And trying extern crate hello; also fails with:

error[E0463]: can't find crate for `hello`

What is the correct way to include a module from another file in the same Rust project?

Short Answer

By the end of this page, you will understand how Rust modules map to files, when to use mod versus use, why extern crate is the wrong tool for files in the same project, and how to correctly split code across main.rs and other source files in a Cargo project.

Concept

In Rust, modules organize code inside a crate. A Cargo binary project usually has src/main.rs as its crate root. Files such as src/hello.rs are not imported automatically just because they exist. You must declare them as modules.

The key idea is:

  • mod hello; tells Rust: there is a module named hello, and its code is in another file.
  • use hello; does not create the module. It only brings an existing path into scope.
  • extern crate hello; is for linking another crate, not another file inside the same crate.

So when you split code into another file, the usual pattern is:

mod hello;

fn main() {
    hello::print_hello();
}

and then in src/hello.rs:

pub fn print_hello() {
    println!("Hello, world!");
}

This matters because real Rust applications are almost always split into multiple files and modules. Understanding Rust's module system helps you structure projects cleanly, reuse code, and avoid import errors.

Mental Model

Think of a Rust crate like a building:

  • main.rs is the front desk.
  • Each module is a room.
  • A file like hello.rs is the contents of one room.
  • mod hello; is the sign that tells the building, "This room exists; go load it."
  • use hello::print_hello; is like saying, "I want quick access to this item from that room."
  • extern crate is for bringing in a completely different building next door.

If you only write use hello;, that is like asking for directions to a room that was never registered in the building directory.

Take Quiz

Syntax and Examples

Basic syntax

Declare a module from another file

In src/main.rs:

mod hello;

fn main() {
    hello::print_hello();
}

In src/hello.rs:

pub fn print_hello() {
    println!("Hello, world!");
}

Why this works

  • mod hello; loads the hello module from src/hello.rs
  • pub fn makes the function public to its parent module
  • hello::print_hello() calls the function through the module path

Using use for convenience

You can also bring the function into scope:

In src/main.rs:

mod hello;
use hello::print_hello;

fn main() {
    print_hello();
}

This is optional. The module still must be declared with mod hello; first.

Common beginner mistake

Incorrect src/hello.rs:

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

This creates a nested module named hello inside the file module hello, which is not what you want.

Correct src/hello.rs:

pub fn print_hello() {
    println!("Hello, world!");
}

The file already represents the hello module.

Step by Step Execution

Consider this working example:

src/main.rs

mod hello;

fn main() {
    hello::print_hello();
}

src/hello.rs

pub fn print_hello() {
    println!("Hello, world!");
}

What happens step by step

  1. Rust starts at main.rs because this is a binary crate.
  2. It sees mod hello;.
  3. Rust looks for the module source in src/hello.rs.
  4. It loads that file as the contents of the hello module.
  5. Inside main, Rust resolves hello::print_hello().
  6. Because print_hello is marked pub, it is accessible from main.rs.
  7. The program runs and prints:
Hello, world!

What went wrong in the original code

Original main.rs:

use hello;

This failed because hello had not been declared yet.

Original hello.rs:

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

This made the actual path effectively behave like hello::hello::print_hello, not hello::print_hello.

So there were two issues:

  • the module was not declared in main.rs
  • the file defined an extra nested module unnecessarily

Real World Use Cases

Rust modules are used everywhere in real projects:

Splitting features into files

A command-line app might use:

  • main.rs for startup
  • config.rs for configuration loading
  • parser.rs for argument parsing
  • logger.rs for logging helpers

Organizing web servers

A web application might have:

  • routes.rs for HTTP route handlers
  • models.rs for data types
  • db.rs for database functions
  • auth.rs for authentication logic

Reusing utility code

You may place helper functions in files such as:

  • string_utils.rs
  • math.rs
  • file_io.rs

Then use them from main.rs or other modules.

Keeping code maintainable

As code grows, one large file becomes hard to read. Modules help by:

  • grouping related functionality
  • reducing clutter
  • making testing easier
  • making code navigation easier
Take Quiz

Real Codebase Usage

In real Rust codebases, developers use modules to create clear boundaries between parts of the program.

Common patterns

1. Feature-based modules

mod auth;
mod config;
mod db;
mod routes;

Each file owns one responsibility.

2. use for cleaner call sites

Instead of writing full paths everywhere:

mod hello;
use hello::print_hello;

fn main() {
    print_hello();
}

3. Public API with pub

Functions, structs, and enums are private by default. Developers choose what to expose:

pub fn connect() {}
fn internal_helper() {}

4. Nested modules for structure

Larger projects often use directories:

src/
├── main.rs
└── services/
    ├── mod.rs
    └── email.rs

Then:

mod services;

inside services/mod.rs:

pub mod email;

5. Guarding internal details

A module may expose a small public surface and hide implementation details. This makes refactoring easier later.

Practical advice

  • Use mod to declare project modules
  • Use use to shorten paths
  • Use pub only where access is needed
  • Keep related code together in the same module

Common Mistakes

1. Using use instead of mod

Broken:

use hello;

fn main() {
    hello::print_hello();
}

Why it fails:

  • use imports an existing path
  • it does not create or declare a module

Fix:

mod hello;

2. Using extern crate for your own file

Broken:

extern crate hello;

Why it fails:

  • extern crate refers to another crate dependency
  • hello.rs is just a module file in the current crate

Fix:

mod hello;

3. Wrapping the file in another mod hello {}

Broken src/hello.rs:

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

Why it fails:

  • the file itself already defines the hello module
  • this creates hello::hello::print_hello

Fix:

pub fn print_hello() {
    println!("Hello, world!");
}

4. Forgetting pub

Broken:

fn print_hello() {
    println!("Hello, world!");
}

Why it fails:

  • functions are private by default
  • parent modules cannot call private items from child modules

Fix:

pub fn print_hello() {
    println!("Hello, world!");
}

5. Expecting Rust to auto-load files

Just creating src/hello.rs is not enough. Rust needs a matching module declaration somewhere, usually in main.rs or lib.rs.

Comparisons

ConceptPurposeUsed forExample
mod hello;Declare a moduleLoad code from another file in the same cratemod hello;
use hello::print_hello;Bring a path into scopeShorter names for existing itemsuse hello::print_hello;
extern crate foo;Link another crateOlder style for external dependenciesextern crate serde;

mod vs use

  • mod creates or declares the module in the crate structure
  • use only creates a local shortcut to an existing path

Same crate vs external crate

SituationCorrect tool
Another file in your src/ directorymod
A dependency from Cargo.tomlcrate path or sometimes extern crate in older code

Direct path vs imported function

StyleExampleWhen useful
Full module pathhello::print_hello()Clear and explicit
Imported functionprint_hello() after use hello::print_hello;Shorter when used often
Take Quiz

Cheat Sheet

Rust module quick reference

Same-project file module

src/main.rs

mod hello;

src/hello.rs

pub fn print_hello() {
    println!("Hello, world!");
}

Call a function from the module

fn main() {
    hello::print_hello();
}

Import for shorter usage

use hello::print_hello;

Rules to remember

  • A file like hello.rs usually corresponds to module hello
  • Use mod hello; to declare it
  • Use use ...; to bring names into scope
  • Use pub to expose items outside the module
  • Do not write mod hello {} inside hello.rs unless you really want nesting
  • Do not use extern crate for files in the same project

Minimal working example

// main.rs
mod hello;

fn main() {
    hello::print_hello();
}
// hello.rs
pub fn print_hello() {
    println!("Hello, world!");
}

FAQ

Why does use hello; not work in Rust?

Because use only imports an existing path into scope. It does not declare or load a module file. You need mod hello; first.

Do I need extern crate for another file in src/?

No. extern crate is for external crates, not files inside the same crate.

Why should hello.rs not contain mod hello { ... }?

Because hello.rs is already the hello module. Adding mod hello {} inside it creates an unnecessary nested module.

Why do I need pub on the function?

Items inside a module are private by default. pub makes the function accessible from main.rs.

Can I use use hello::print_hello;?

Yes, but only after the module exists. Usually that means declaring mod hello; first.

What file names does Rust look for with mod hello;?

For a simple module, Rust looks for hello.rs or a directory-based module form depending on project structure.

Should I put shared code in main.rs or lib.rs?

If code is meant to be reused across binaries or tests, lib.rs is often a better place. For small binary-only projects, main.rs is fine.

Take Quiz

Mini Project

Description

Build a tiny Rust command-line program that separates greeting logic into its own module file. This demonstrates the correct way to declare a module from main.rs, expose a public function, and call it from the main program.

Goal

Create a Cargo project where main.rs loads a greetings.rs module and prints a message using a public function from that file.

Requirements

  • Create a greetings.rs file inside src/
  • Declare the module from main.rs
  • Add a public function that returns or prints a greeting
  • Call that function from main
  • Make sure the program builds and runs with Cargo
Take Quiz

Keep learning

Related questions

View more