A discussion about improving "import" codes

In Rust 2018, mod.rs is no longer needed. But module’s code is still ugly. Currently:

 ├── a
 │   ├── a1.rs
 │   ├── a2.rs
 │   └── a3.rs
 └── a.rs

We must write codes like this:

// a.rs

pub mod a1;

mod a2;

pub mod a3;

If we need to adjust modules and filenames, it will be tedious work.

Is it possible to improve its design?

For example:

// a1.rs

pub self;


// a2.rs

// write nothing to be a private mod


// a3.rs

pub self;

Then, remove a.rs.

It will be:

 └── a
     ├── a1.rs
     ├── a2.rs
     └── a3.rs
1 Like

In Rust, the ability to conditionally include a module (i.e. #[cfg(feature = "foo")] mod foo;) is important. With “module discovery”, this is no longer possible.

I want to note something a lot of people (including myself!) have tended to miss: Rust is not the only language to require explicitly including module files. C/C++ have the mess of header files required, and compiling and linking each individual source file. Python and JavaScript each require including modules/files explicitly as well.

Interestingly enough, even Java doesn’t do automatic file discovery. If you’re using javac directly, you have to compile each .java into a .class and then bundle them all into the .jar. It’s just that all common build tools automatically do this for every file in a directory.

rustc, the actual Rust compiler, actually does the module discovery by way of the mod declarations. The cargo build tool only has to invoke rustc on the root .rs file.

3 Likes

I suppose this could be worked around with #![cfg(feature = "foo")] inside the module.

I would still prefer to have some external signal to load a file, e.g. use crate::a1; somewhere in the code.

1 Like

The difference is that with #![cfg] the module still has to parse, whereas it doesn’t with #[cfg]. If, e.g., you had a module that contained optional async.await code, parsing it even with #![cfg(FALSE)] will fail on any currently stable compiler. Alternatively, if the mod declaration is #[cfg]d, you can restrict parsing of that module to users who opt in, and still support earlier compilers for those who don’t.

1 Like

How about giving a default declaration?

├── a
│   ├── a1.rs
│   ├── a2.rs
│   └── a3.rs
└── a.rs

For example, if a.rs is not exist or empty, the modules in a are public (or private) by default.

We can still write codes like #![cfg]. And it will economize some codes.

And, is it possible to change rustc’s behaviour so that codes that contain async.await can be compiled successfully with #![cfg(FALSE)] (declared in the module)?

I’m not sure. Is it a innocent idea?