Why is a double use of the module name needed so that the compiler finds it?

Hello,

I am learning Rust from the book and made a test project to understand modules.
My directory structure and the important files are as follows:
My question: why do I need to use

use crate::module1::module1::greet;
use crate::module1::submodule1::submodule1::greetsubmodule1;

instead of

use crate::module1::greet;
use crate::module1::submodule1::greetsubmodule1;
hello_cargo
- Cargo.lock
- Cargo.toml
- src/
-- lib.rs
-- main.rs
-- module1.rs
-- module1/
--- submodule1.rs
- target
-- (not altered- standard stuff, unimportant.)
// main.rs
pub mod module1;
use crate::module1::module1::greet;
use crate::module1::submodule1::submodule1::greetsubmodule1;

fn main() {
    greet();
    greetsubmodule1();
}
// module1.rs

pub mod submodule1;
pub mod module1 {
    pub fn greet() -> () {
        println!("greetings from module1.");
    }
}

// submodule1.rs
pub mod submodule1 {
    pub fn greetsubmodule1() -> () {
        println!("greet from submodule1.");
    }
}

When you are putting content into a new file and using pub mod module1 in main – you are creating crate::module1, when you are writing pub mod module1 { … } – you are creating another, nested, module.

That's inherent ambiguity of English about “exclusive” vs “inclusive” “or”: book says you may do one or another, but you assumed you can do both… well, it's not a crime to do both – but compiler wouldn't magically recognize that it's “silly” to have module1 inside of module1 (compilers tend to not think, but follow instructions) thus you end up with module1::module1.

3 Likes

thanks!

Because your file should look like this. You should not create mod module1 inside module1 file

// module1.rs
pub mod submodule1;

pub fn greet() -> () {
    println!("greetings from module1.");
}

You also maybe create module in wrong file. I see you have both: main.rs and lib.rs
main.rs is binary crate, lib.rs is library crate. If you have lib.rs file, you create all modules in lib.rs file. This is how your files should look.

// lib.rs
pub mod module1;
// main.rs
use hello_cargo::module1::greet;
use hello_cargo::module1::submodule1::greetsubmodule1;

fn main() {
    greet();
    greetsubmodule1();
}

You use "use hello_cargo::module1::greet;", because main.rs and lib.rs are diffirent crates, so you can't call modules in lib.rs from main.rs using "use crate::module1::greet;"

// module1.rs
pub mod submodule1;

pub fn greet() -> () {
    println!("greetings from module1.");
}
// submodule1.rs

pub fn greetsubmodule1() -> () {
    println!("greet from submodule1.");
}

In Rust, files don't say what module they are (this is a completely different approach than in languages like Java and Golang that have a package keyword).

Instead, a parent source code file declares with mod what child modules are supposed exist inside it, and then Rust finds files that match the expected path.

FWIW, detecting that someone is doing this would be an excellent lint to have.

Theres a clippy::module_inception lint for this already.

2 Likes

I once intentionally created a module with the same name as its parent module, but backed off when Clippy complained about it.

1 Like

I'm really glad to hear that clippy already has this logic. I suspect that for things that fall under "mistakes only a very new newcomer would make" the lints should live in rustc itself, as that's the group of people most likely to not know about clippy yet.

1 Like

Best lint name ever?! Genius.