Why would rust use both mod and use?

I got confused by rust mode and use ;

According to the documentation ` Declaring modules: .Rust module based on filesystem hierarchy. We need to explicitly include the module;
Thus this is what we write

use module_d::echo_module_d;
pub mod module_d;

pub fn echo_module_c() {
    echo_module_d();
}

Is there are a more concise way to include module ?

use module_d::echo_module_d;
// :roll_eyes:  Is possible to remove this line ? 
// pub mod module_d;

pub fn echo_module_c() {
    echo_module_d();
}

Base on what I know

*echo_module_d can be found through module rules.
*echo_module_d is private by default, if change to public by default ,there is no need for declare pub module xxx

Question
Can mod integrate intouse module_d::echo_module_d ,so we just need one line of code to do these stuff?

No. Modules are declared explicitly.

3 Likes

although it's true(well...), the compiler still read mod c to create the namespace self::c that is corresponding to the file, rather than automatically building the tree of namespaces based on existing knowledge of filesystem.

Not necessarily. Think of e.g. unit test modules, which are generally defined in the same file as their parent module whose functionality they're testing.

4 Likes

The use line in your example is optional:

pub mod module_d;

pub fn echo_module_c() {
    module_d::echo_module_d();
}
3 Likes

The mod and use statements do completely different things.

Declaring a module via mod places the module in the hierarchy at the point of declaration (whether it is inline or a separate file makes no difference. A pub mod declaration makes it accessible to the level above (and possibly further if the parent is also pub).

A use declaration brings the names of the items in the named module into scope so they can be used without fully qualifying them. This means you could avoid use statements if you want to fully qualify the names of everything you need. A pub use declaration does the same but additionally makes those items look like they are defined in the current module when viewed by code outside the current module (they remain available via their original names as well). It is also possible to use individual items instead of entire modules.

Edit:
To actually answer your question:

No, that would needlessly pollute the namespace of all the modules above in the hierarchy. In more complex examples you would run into naming conflicts and ambiguity.

5 Likes

The mod statement tells the compiler about a file it hasn't seen before. The use statement lets you import things the compiler already knows about into the local scope.

5 Likes

yeah, I just don`t understand why not do these stuff automatically,which will be more convenient for up developer.

Well, one reason I can think of is that in Rust, compiling code can have needed β€œside effects” on the rest of the program even if there are no uses referring to it. In particular, modules can contain impl blocks (trait or inherent) that are then used elsewhere in the program. Therefore, it is important that which source files are part of the crate be clearly defined.

  • If the source files were found by reading everything in the src/ directory, then then platform-specific or feature-specific conditional compilation would be harder: every affected file would need a #![cfg] at the top.

  • If the source files were found by looking for all modules named in use declarations, then you might accidentally lose a file and its impl contents simply because it doesn't appear in any use. Explicit use module; (currently useless in actual Rust) could be added intentionally, but that's still something that could be forgotten β€” and if it is necessary, why shouldn't it be a distinct syntax, mod?

Also, mod items are where the visibility of a module is specified β€” it'd need to be done by a file-level inner attribute instead.

Also, the current system has the advantage that every item in a module, including child modules, is explicitly declared β€” if you see foo::bar::baz, you can always read bar.rs to find out what baz is. If modules were implicit, you'd have to read bar.rs and check if there's a bar/baz.rs.

None of these are unsolvable problems β€” but I hope I've pointed out some ways in which Rust's current design fits together with itself and isn't just arbitrary verbosity.

7 Likes

Could you please give some specific code example about namespace pollute ?

It`s really helpful,ths

With regard to namespace pollution, I was at first thinking you were suggesting an automatic and implied use module_name; for any child module defined in a parent. This would pollute the namespace of the parent, and by extension all the modules above it.

But to consider your proposal, let's imagine a Rust with only one of mod or use and not both.

First off, keep in mind a module declaration is only used once per module in the entire application, not per file, so your proposed changes would result in almost no savings in line count. (not that saving lines is even desirable, if it results in a loss of flexibility) Secondly, use is entirely optional, since you can always refer to any item via its fully qualified name.

First let's try a Rust with mod only:
There are two cases here - either we keep mod as a once per application thing or we allow it to appear in multiple locations.

  1. In the first case, we are done...just refer to everything fully qualified all the time. That certainly seems like a net loss on developer ergonomics, but does work.

  2. In the second case, we now have ambiguity because items can be defined in multiple equally valid locations (keep in mind the crate, not the file, is the unit of compilation). This is clearly no good.

Second, let's try use only. Again, we run into the problem of multiple declarations. One of them would have to use an alternate syntax to be the "actual" declaration. This elminates pretty much all the potential savings and still has the problem mentioned by @kpreid of needing code which is not marked by a use statement anywhere.

3 Likes

This was actually discussed extensively around the module and path resolution rework for Rust 2018. It would certainly be technically possible.

However, in the end, it was decided not to change to "automatic mod". There were enough voices that wanted it left manual that it wasn't clearly worth changing.

(There's a couple of nice things about manual, like random untracked files not getting picked up automatically. And because #[path] exists, some people don't want things showing up based on the path in the filesystem. And some people didn't like the extra filesystem calls, because of network filesystems or something like that. You can find the RFC conversations for full details.)

8 Likes

mod works the same way as fn, struct, and enum. It defines a new named thing. If you want to use this thing elsewhere, you use it.

You wouldn't want to redefine the same function in every file. You define the function once, and then just refer to this one-and-only definition either via use or using its absolute path. Same with modules. You define the module once (using mod), and then to refer to this one-and-only definition elsewhere with use, or through module's absolute path.

5 Likes

Thanks bro :+1: . you free me out.