Rust code management is very confusing

Could someone point me to sources where I can find out more about packages, crates and modules and what is the difference between them?
I looked in the Rust Book but the information was very vague and other sources on the internet seemed to be outdated, it seemed to be about rust 2015. I would also like to know what is the difference between lib.rs and mod.rs.
Thanks.

4 Likes

I recall a similar frustration when I first started out. The language has made a few improvements. I would include what may have been a relatively recent update to the book’s chapter 7 that takes you through the series of stepwise choices in how to manage β€œa growing project.

You mentioned you had looked at the book. Perhaps the shared link is from a different section or a more recent version of the book?

5 Likes

It is exactly this book of your link, it does not inform very well how the modules work with the file mod.rs and lib.rs.
Some blog articles use lib.rs and others use mod.rs, in the end I don't understand which one to actually use.

mod.rs proved confusing to people, so it’s been on the way out for a few years now. It’s not recommended in new projects.

In general, if you have some file foo.rs that contains the statement mod bar;, the compiler will read the file foo/bar.rs as the contents of that module.

lib.rs (for reusable libraries) and main.rs (for executable programs) are special and don’t quite work the same way. They’re the file that the compiler looks at first, and they act a bit like they're one directory higher than they really are: Instead of lib/foo.rs or main/foo.rs, a mod foo; statement here will read in foo.rs in the same directory.

It’s important that you only ask for each source file to be read in once. To access a module anywhere other than the place it’s read in, you need a use statement.

8 Likes

crates and packages are basically the same thing. They're whole programs or libraries.

modules are parts of crates. They usually represent individual files.

4 Likes

More on the difference between crates and packages: A package is a Cargo concept, whereas a crate is a Rust concept. Packages can contain multiple crates:

  • src/lib.rs
  • src/main.rs
  • Every test file in tests/
  • Every benchmark file in benches/
  • doctests are also separate crates
4 Likes

I'm still learning, and it took me a while to figure out what now seem some elementary things about modules in a crate, but I finally got to a simple template (which may not be 100% correct in its details, but is workable).

First, you can forget about mod.rs.

The crate is the base folder:

cratename
|--- Cargo.toml
|--- src

For a binary (i.e. with a main.rs), here is the layout of one of my projects:

src
β”œβ”€β”€ chardrip.rs
β”œβ”€β”€ documents.rs
β”œβ”€β”€ lib.rs
β”œβ”€β”€ main.rs
β”œβ”€β”€ tokenisers.rs
└── trigram_reader.rs

main.rs is the start of the executable. Here is where I use modules to be called in the main function.

Rust automatically puts any code in a file into a module named after that file: e.g. the code in "chardrip.rs" is in a module called chardrip.

Important step: list all your modules in lib.rs with mod (I don't put anything else in lib.rs):

pub mod chardrip;
pub mod documents;
pub mod tokenisers;
pub mod trigram_reader;

Then all your modules can use other modules in your crate, e.g. in "documents.rs" I have:

use super::tokenisers;
use super::trigram_reader;

This step of putting all the mod statements in "lib.rs" was the one that solved a lot of problems I was having.

(Even after going through the book and reading different posts, I could not make this multi-file setup work at first: for instance, I was trying to put mod tokenisers in other files, to "import it", and that is incorrect. mod statements I think can only appear once - and I put them all in "lib.rs".)

8 Likes

For whatever reason it took me a year to figure out that simple scheme. Every time I came to splitting a new little project into modules I got totally confused, despite asking and reading about it here along the way.

If only the book gave that as an example. Perhaps it does now, I'll have to check.

That "list all modules in lib.rs" grates on my nerves, seems redundant. But it is what it is.

4 Likes

It sometimes comes in handy, though. If I'm doing a big refactor that's going to break everything, I'll generally comment out all the modules and then bring them back one-by-one. It helps organize the work.

2 Likes

It also allows you have an entire file included conditionally. If all files were automatically compiled then every architecture or feature dependent file would need to contain cfg directives to limit when it is built.

4 Likes

I should stop hoping between languages. Then I might remember were I am and what is what.

1 Like

Ignoring mod.rs and using lib.rs worked very well.
Thanks for everyone for the answers.
The book needs clarification on how to use modules and crates, this leaves many people frustrated.

1 Like

The issue above is just one example of many needlessly confusing things about Rust.
Too mush time is spent figuring out things like :
(1)
For the cargo created the directory structure :
crate_name
|--- Cargo.toml
|--- src
does Rust statement 'extern some_name' cause rustc to import crate 'some_name'
OR does it only act as a reference point for some future 'use' statement
?

(2)
Why doesn't this work :
'use crate_name::{ module_name1, nodule_name2, ....} ; '

(3) or this :
'use crate_name::
{ module_name1 (fn_name1, fn_name2..) ,
module_name2 (fn_name1, fn_name2..) ,
....
} ; '

I love Rust, but it does have a needlessly long (and steep) learning curve.

1 Like

Please try to avoid attributing your personal opinions to the entire community. Learning the idiosyncrasies of a new programming language is a frustrating but inevitable process. Everyone approaches Rust with a different background, and will have different opinions about what's confusing, whether that confusion is necessary, and how hard it is to learn each novel thing.

Because it was designed to work a different way. Rust has a very open design process, so it's likely a large number of people believe the current solution is at least sensible, if not ideal. It's also still under active development-- If you can make a reasonable case for why this should be allowed, feel free to lobby for the change over on internals.


I'll note that syntax extremely similar to what you list here does work:

use crate::{ module_name1, nodule_name2, ....} ;

use crate::{
    module_name1::{fn_name1, fn_name2} ,
    module_name2::{fn_name3, fn_name4} ,
....
};
4 Likes

Maybe.

To see how complex and confusing things can be try building a C or C++ program with loads of source and header files and library dependencies. Throw in the tools they have to help out, like makefiles, cmake, auto tools, qmake etc.

The Rust setup is an oasis of simplicity, common sense and obviousness by comparison.

Confusion is in the eye of the beholder.

5 Likes

Isn't the answer "don't use 'extern some_name'"? I thought that syntax was a relic of rust 2015.

3 Likes

Well extern some_name isn't actually valid syntax, it's extern crate some_name. But you're correct - other than the very occational situation when it is needed (e.g. linking with liballoc, and I think it's necessary for the crates inside rustc itself) it's basically useless.

2 Likes

Confusion is in the eye of the beholder...
try building a C or C++ program with loads of source and header files and library dependencies. Blockquote

I do this routinely...it's takes about a 20-minute read, to learn how to do it, and only requires a C++ compiler.

Throw in the tools they have to help out, like makefiles, cmake, auto tools, qmake etc. Blockquote
I never mess with any of this stuff because I've never needed any of them...every so often I look at a makefile if I run into compile problems but that's about it .

Confusion is in the eye of the beholder... Blockquote
absolutely !

1 Like

Let's just say that is not my experience with C/C++ over the last few decades. Dealing with code that is supposed to run on multiple operating systems and hardware architectures. Code form disparate sources. Migrating code that was not written or built with portability in mind to new platforms and so on. It's a huge mess. That is before we get into cross-compiling anything.

1 Like

You'll almost certainly see it in the wild. Unless there is path renaming going on, a non-inline module my_module could be contained in:

src
└── my_module.rs

Or

src
└── my_module
    └── mod.rs

It's an error to have both.

1 Like