this post was submitted on 08 Nov 2023
13 points (88.2% liked)

Rust

5938 readers
2 users here now

Welcome to the Rust community! This is a place to discuss about the Rust programming language.

Wormhole

[email protected]

Credits

  • The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)

founded 1 year ago
MODERATORS
 

I am pretty new to Rust and I find the docs can be a little confusing sometimes so I'd be really thankful for people who can help me understand this.

So for my uni project we are doing part of it in Rust. and I am having issues understanding how the modules function. So here is my example program.

Main.rs does some stuff and then passes a pointer into a function from a separate Rust file. That function then does some more things and passes a new variable into a function form another separate Rust file as a pointer.

main.rs has mod separate_file1; and then in separate_file 1 there is a mod separate_file2;.

The confusion is that separate_file2 does not read from the same directory as main.rs despite being in it. Instead it looks in a sub-folder under the same name.

If someone could explain to me why it works like this and what the best practice is for using modules I would be really thankful. It's made me struggle a lot.

top 6 comments
sorted by: hot top controversial new old
[–] [email protected] 13 points 1 year ago (1 children)

If you want your module to contain submodules, it needs to go into a folder. That folder needs to be named like the module.

It's explained pretty well in the book, imho: https://doc.rust-lang.org/stable/book/ch07-02-defining-modules-to-control-scope-and-privacy.html

So, for your example, the file structure could for instance be
src/main.rs
src/separate_file1.rs
src/separate_file1/separate_file2.rs

An alternative layout that I think is more common would be
src/main.rs
src/separate_file1/mod.rs
src/separate_file1/separate_file2.rs

Or, if you think separate_file2 could contain submodules at some point, maybe
src/main.rs
src/separate_file1/mod.rs
src/separate_file1/separate_file2/mod.rs

[–] [email protected] 3 points 1 year ago (1 children)

Yeah, it's tricky that the file for a module is in a subfolder under the file that declared it, unless the file that declared it is named main.rs, lib.rs, or mod.rs in which cases the module file is in the same folder, not in a subfolder. There is logic to it, but you have to connect multiple rules to get there.

We see in the examples above that a module named whatever can be in whatever.rs or in whatever/mod.rs and you get the same result. mod.rs is a special name with a special lookup rule.

whatever/mod.rs whatever/submodule_of_whatever.rs works exactly the same as whatever.rs whatever/submodule_of_whatever.rs. We use mod.rs so we don't have to have both a folder and an .rs file with the same name. But that leads to the special exception where submodules declared in mod.rs are defined by files in the same folder as mod.rs.

main.rs is like the mod.rs of the entire crate. main.rs has a special rule where it's in the same folder as its submodules, instead of the normal rule where submodules are in a subfolder.

lib.rs fellows the same special rule as main.rs. (You use main.rs to define an executable, lib.rs to define a library.)

[–] [email protected] 5 points 1 year ago (1 children)

It is not that tricky if you shift your perspective. Rather than thinking about files importing files (like you would in other languages), think about the crate as a tree of modules. Then the rules are simple. main.rs and lib.rs are the entry points to the crate and define the root module. Other modules are nested under this and may exist in a file that matches the modules path in the tree. So a module at crate::foo::bar::baz can live in either foo/bar/baz.rs or foo/bar/baz/mod.rs. Or be defined as a inline module in the parent module (which may also be defined inline in its parent).

This also means if you know the file path you know where it will exist in the tree, a/b/c/d.rs will be at the crate path a::b::c::d.

[–] [email protected] 4 points 1 year ago

Yes, I like your explanations and I agree that's the way to think about it. But either way you have some special exceptions because main.rs maps to crate instead of to crate::main, and a/mod.rs maps to crate::a instead of to crate::a::mod. I know that's the same thing you said, but I think it's worth emphasizing that the very first file you work with is one of the exceptions which makes it harder to see the general rule. It works just the way it should; but I sympathize with anyone getting started who hasn't internalized the special and general rules yet.

[–] [email protected] 11 points 1 year ago

Best to not think of files as modules. Instead a rust crate is just a tree of modules with the src/main.rs or src/lib.rs being the main entry point.

Inside these files you define the module structure you want like:

mod foo {
    mod bar {}
}

This creates two modules, crate::foo and crate::foo::bar. Now, because you don't want to have all your code in main.rs/lib.rs rust lets you move the module contents to separate files. But the location of the file needs to match its location in the module structure - from the crates root (not the file it was declared in).

So when you call mod foo; from src/main.rs it will look for src/foo.rs or src/foo/mod.rs (and you can only have one of these). And the foo::bar - no matter if that is declared in src/main.rs (as above) or inside src/foo.rs or src/foo/mod.rs, it will always look for the bar module contents inside src/foo/bar.rs or src/foo/bar/mod.rs as it is nested inside the foo module - not because the file is next to the current one.

This means if you had this inside main.rs/lib.rs:

mod foo {
    mod bar {
        mod baz;
    }
}

Then it will look for the baz module contents inside src/foo/bar/baz.rs or src/foo/bar/baz/mod.rs - even though those might be the only two files you have.

[–] [email protected] 2 points 1 year ago

What do you mean by "doesn't read from the same directory"? Is part of your application's function to read in data from files in your project's directory tree?

Without seeing the directory structure of your project, or some more actual code samples its hard to understand how to help 😅