60

I have a Cargo project consisting of three files in the same directory: main.rs, mod1.rs and mod2.rs.

I want to import functions from mod2.rs to mod1.rs the same way I would import functions from mod1.rs to main.rs.
I've read about the file structure required but I don't get it - naming all the imported files mod will lead to minor confusion in the editor and also this just complicates the project hierarchy.

Is there a way to import/include files independently of directory structure as I would in Python or C++?

main.rs:

mod mod1; // Works

fn main() {
    println!("Hello, world!");
    mod1::mod1fn();
}

mod1.rs:

mod mod2; // Fails

pub fn mod1fn() {
    println!("1");
    mod2::mod2fn();
}

mod2.rs:

pub fn mod2fn() {
    println!("2");
}

Building results in:

error: cannot declare a new module at this location
 --> src\mod1.rs:1:5
  |
1 | mod mod2;
  |     ^^^^
  |
note: maybe move this module `src` to its own directory via `src/mod.rs`
 --> src\mod1.rs:1:5
  |
1 | mod mod2;
  |     ^^^^
note: ... or maybe `use` the module `mod2` instead of possibly redeclaring it
 --> src\mod1.rs:1:5
  |
1 | mod mod2;
  |     ^^^^

I can't use it as it doesn't exist as a module anywhere, and I don't want to modify the directory structure.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Neo
  • 3,534
  • 2
  • 20
  • 32

3 Answers3

98

All of your top level module declarations should go in main.rs, like so:

mod mod1;
mod mod2;

fn main() {
    println!("Hello, world!");
    mod1::mod1fn();
}

You can then use crate::mod2 inside mod1:

use crate::mod2;

pub fn mod1fn() {
    println!("1");
    mod2::mod2fn();
}

I'd recommend reading the chapter on modules in the new version of the Rust book if you haven't already - they can be a little confusing for people who are new to the language.

Joe Clay
  • 33,401
  • 4
  • 85
  • 85
  • 6
    I read that chapter, but didn't understand from it I could do what you describe. – Neo Oct 19 '17 at 12:20
  • 1
    @Neo: No problem! I find the module system makes sense once you've got a feel for it, but there's definitely a bit of a learning curve - [there's currently work being done to simplify things a bit](https://github.com/rust-lang/rfcs/pull/2126). – Joe Clay Oct 19 '17 at 12:22
  • @Neo: I'd also recommend this blog post on how to mentally model the module system: http://manishearth.github.io/blog/2017/05/14/mentally-modelling-modules – Joe Clay Oct 19 '17 at 13:44
7

Every file is a module and cannot import another without creating a new nested module.

a. Define modules in module index file

As @giuliano-oliveira's answer recommends.

Add pub mod mod1; pub mod mod2; in src/lib.rs / src/main.rs / src/foo/mod.rs.

b. Use #[path]

main.rs

#[path = "./mod2.rs"]
mod mod2;

fn run() { mod2::mod2fn() }

Why?

This is a common pitfall for new Rust devs and understandably so.

The reason for the confusion comes from an inconsistency in behavior of mod X for files in the same folder. You can use mod X in lib.rs which appears to import a file adjacent to it, but you can't do the same in mod1.rs or mod2.rs.

The code of every file belongs to a module. The full path of the file's module (e.g. foo::bar::baz) rather than the location of the file, determines how it resolves mod X. You can think of it as every module having a fixed spiritual home, but it may have members defined further up in the hierarchy (e.g. src/lib.rs might contain: mod foo { mod bar { pub fn hello() {} } } - although then you cannot use mod foo; alone in lib.rs).

In main.rs, you are in the top-level module crate.

mod mod1; creates a new module mod1, and adds the contents of ./mod1.ts to that module.

So all code inside ./mod1.rs is inside the crate::mod1 module.

When you call use mod2 inside ./mod1.rs, it sees that it is inside crate::mod1, whose spiritual home dir is src/mod1, and looks for either:

  • src/mod1/mod2.rs
  • src/mod1/mod2/mod.rs

The complexity comes from allowing modules to be directories and also files, instead of forcing each module to be its own directory (maybe trying to avoid the Java folder structure) which would have removed the ambiguity.

The key thing to remember is that lib.rs and mod.rs are special files that behave differently to other files in a directory.

They will always be in the module described by the parent folder path (e.g. src/foo/bar/mod.rs = foo::bar) while all other files belong to their own modules (src/foo/bar/baz.rs = foo::bar::baz).

The Rustonic Way

Rust has some opinions on this.

Using mod.rs is not recommended anymore, which is good because it has this special behavior from its siblings. But lib.rs and main.rs are still special.

If you want to put tests alongside your code (foo.rs + foo_test.rs), it's recommended you don't. I don't like this, so I use the path thing above, which I think is fine for tests because they are not exported anywhere. Having to declare tests in the module above feels wrong, and I don't like having to use foo/test.rs either.

vaughan
  • 6,982
  • 6
  • 47
  • 63
  • 3
    Having **"Use #[path]"** as a big bold header gives the wrong impression to a skimming viewer when the content of your answer says the opposite. I would personally avoid mentioning it at all. – kmdreko Sep 08 '21 at 16:45
  • The final question from OP was "Is there a way to import/include files independently of directory structure as I would in Python or C++?". Using path is in fact the right answer to this specific question. So I think it's actually more correct than the accepted answer. Worth mentioning anyway. – vaughan Sep 08 '21 at 22:27
  • If being "technically correct" is your angle, I'd still consider your answer deficient. It doesn't explain how `#[path = ...]` works and why its not recommended. Explaining that `main.rs`/`lib.rs`/`mod.rs` are special and introducing a slightly different structure (i.e. `foo.rs` instead of `foo/mod.rs`) make this a decent answer. I just wish you would reword some of it. I've seen too many newcomers use `#[path = ...]` instead of traditional imports and come to SO understandably confused why their code isn't working. – kmdreko Sep 08 '21 at 23:43
  • Okay, I have reworded it a bit. – vaughan Sep 09 '21 at 10:46
  • I don't understand what is wrong with the #[path] approach. It is the only way I could get things to work. – PaulCommentary Jan 29 '23 at 06:58
  • I like the idea of not having many, many mod.rs riles in a Rust project, so that's another reason for liking #[path]. Must admit I am confused about the @giuliano-oliveira's approach. – PaulCommentary Jan 29 '23 at 07:00
6

If you don't want your mod statements all in your main file (eg: in main.rs you won't use some public members inside some module, in this example it is mod2) you can do the following:

structure your src this way:

main.rs
my_module:
  mod.rs
  mod1.rs
  mod2.rs

then you can just mod my_module and use my_module::mod1, like so:

main.rs:

mod my_module;
use my_module::mod1;

fn main() {
    println!("Hello, world!");
    mod1::mod1fn();
}

my_module/mod.rs

pub mod mod1;
pub mod mod2;

my_module/mod1.rs

use super::mod2;

pub fn mod1fn() {
    println!("1");
    mod2::mod2fn();
}