Historically, Rust has been designed from the beginning as a language for writing scalable, reliable system programs. The module system was created for strict namespace separation, code isolation, and organization of large projects without name conflicts. Modules allow structuring code, hiding implementation details, and managing the public API.
The problem is that as the project grows, the question of component isolation, code reuse, and visibility management arises. Errors are often related to the incorrect use of keywords pub, pub(crate), as well as the lack of or improper organization of the directory and file structure.
Solution: in Rust, a module is declared with the mod keyword; files and directories structure the namespace, emphasizing the hierarchy. Everything is private by default, and the keywords pub, pub(crate), pub(super) allow managing visibility. Correct usage of exports, aliases, as well as proper project splitting into modules makes the code scalable, maintainable, and safe.
Code example:
// src/lib.rs mod network; pub use network::client::Client; // src/network/mod.rs pub mod client; // src/network/client.rs pub struct Client { pub name: String, }
Key features:
What happens if the directory structure does not match the module definitions?
The code will not compile: Rust expects strict matching of files and modules (for example, if mod foo is declared, there must be a file foo.rs or a directory foo/mod.rs).
Can pub(crate) be used to hide implementation from external consumers but be accessible to all modules within the crate?
Yes, pub(crate) makes the item visible in any module of the same crate but invisible to external projects using this crate as a dependency.
How to import functions from a deeply nested module into the root file without public re-exporting (pub use)?
Through a direct reference in the hierarchy: crate::module::submodule::function. Without pub use, they will be available only within the crate.
In the network module, all components of the server, client, and protocol parser are in one file network.rs. Everything is declared pub, and internal code is directly accessed for testing. Over time, unnecessary coupling arises, and it becomes difficult to separate the public interface from the implementation.
Pros:
Cons:
The network is split into submodules: client, server, protocol. Only public interfaces are exported from the network, the API is compact, and details are encapsulated. Tests for each module are isolated.
Pros:
Cons: