Rust code management is very confusing

Am I the only person who read the Rust book and thought the module system made perfect sense?

Edit:
I find Rust's module and file system correspondence simple and logical.

8 Likes

Likely it is.

One of the first confusions is that a bunch of code in a file can become a module in two ways:

a) It's wrapped up in a "mod" statement surrounded by curly braces.

b) Some other file, somewhere else, says it is with a "mod" statement naming the file that contains that bunch of code.

Then comes the thing that the other file turning a bunch of code into a module, with "mod" had better be main.rs or lib.rs depending on where that file containing the bunch of code is.

Then, if that bunch of code makes itself into a module by wrapping up in a "mod" statement and braces, that changes everything again.

This is all reasonable but it is not obvious for those coming from other environments.

1 Like

I think I became stuck in not appreciating that mod and use are slightly different concepts to what I'm familiar with (e.g. each slightly different to Ruby's require or Java's import), and overlaid on that is Rust's special treatment of lib.rs (or main.rs).

Looking in section 7.5 of the book, mod gets described: "Using a semicolon after mod front_of_house rather than using a block tells Rust to load the contents of the module from another file with the same name as the module."

Having read that, I tried to make another file, hotel.rs, where I wanted to use front_of_house. I start with mod front_of_house;

Rust complains: there's no file called hotel/front_of_house.rs...

This is perplexing - I just wrote that in lib.rs! Looking in the book, I see front_of_house.rs has a mod hosting statement, so surely you can reference modules in other modules? ... I remember spending some dark times trying to write variations on mod super::front_of_house, to enable my new file to find its sibling.

And then (after some days of living in a single file) I realised I had the wrong idea. Rust treats lib.rs as a special case - those top-level mods have to be in lib.rs (I believe???), and you use them from your other files.

(I have later learned about submodules in folders, which is how front_of_house can have a mod hosting statement in it.)

I'm not sure, but I wonder if section 7.5 could be improved with a comment about this: explaining that top-level mods must live in lib.rs because lib.rs is special.

Yeah, I finally got there!

2 Likes

? Can Rust accept relative or absolute paths in a use statement (or some other equivalent) :
use "~/my_directory/.../my_file.rs" ;
or
use "./my_directory/my_file.rs" ;

1 Like

With the path atttribute you can specify a relative or absolute path to a module's source file:

#[path = "/path/to/my_file.rs"]
mod my_file;

// or

#[path = "../my_directory/my_file2.rs"]
mod my_file2;

But using the “default” paths (i.e., without the path attribute) has the advantage that you can easily map from module paths to filesystem paths. For example, by default the file src/foo/bar/baz.rs will always correspond to the module crate::foo::bar::baz.

3 Likes

Ahhhh , #[path= ...] eliminates all confusion as to what I specifically want rustc to use.

I see advantages to using both #[path= ...] and crate :: ...
but some how got the (mistaken?) impression that crate:: can only be used when cargo or rustc is called within a specific /src sub-directory of some crate.

i.e. If all my crates are stored in directory
`~/all_my_crates/
-- crate1/
-- src1
--crate2/
--src2
etc

and I decide to run, within my home directory ~/ the command : rustc my_file.rs

If my_file.rs contains statement crate1:: ..
It seems to me rustc won`t find crate1::
BUT using #[path= ] allows rustc to find it

1 Like

If you're running rustc directly (i.e. without cargo) and want to link to other crates, you would compile those crates first, then use the --extern flag to tell rustc the location of the compiled library. For example:

rustc --edition 2018 --extern crate1="/path/to/libcrate1.rlib" my_file.rs

(rustc also has -L and -l arguments for library paths, similar to the C compiler arguments.) Most Rust development is done using cargo, which passes these arguments automatically based on the dependency list in Cargo.toml. You can use the cargo -v flag to see the rustc commands that Cargo runs.

Making something a module is very different from making it a crate. A mod item like this:

mod foo;

is shorthand for:

mod foo {
    /* entire contents of foo.rs */
}

Among other things, this means that if you include the same module into your program twice, two copies of the source code will be included into your program and compiled into two modules that are identical except for their names. (This is usually bad! It will make your program take longer to compile, and can create subtle or not-so-subtle bugs.)

2 Likes

After I understood that mod.rs is something from the old rust 2015 it was really simple. It turns out that there is a lot of material from the old rust and you "learn wrong" using it.

Does the bar module inherit all pub field/modules of the foo module?
E.g.
Given

foo:
    pub mod bar;
    pub mod baz;

Is it possible in mod bar (bar.rs) to access baz variables implicitly (baz.some_var) or are we required to use super (super.baz.some_var)?

1 Like

No, but you can add the following import to access the things in baz as baz::some_var.

// bar.rs
use super::baz;
3 Likes

If you have:

   foo.rs
   foo(directory)
      |
      | bar.rs
      | baz.rs

This is equivalent to:

mod foo{

    mod bar{
    }

    mod baz{
   }
}

Something that seems to surprise some people is that both bar and baz can access everything in foo, both public and private. This is spelled out in the Book, but it seems to run counter to people's expectations. The siblings bar and baz can only access each other's public items.

This means that a deeply nested module has access to everything above it in the hierarchy, no matter any access restriction.

Additional note: Whether or not bar and baz themseleves are public or private does not affect access from any sibling. Just like a private function can access another private function in the same module, any item in a module has access to all sibling items regardless of privacy level.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.