ProgrammingRust backend developer

How is the module system organized in Rust and how to properly organize large projects considering nested modules, visibility of elements, and file system structure?

Pass interviews with Hintsage AI assistant

Answer.

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:

  • By default, items are private; explicit pub specification is required for export.
  • The structure of modules is reflected in the file system (mod.rs and submodules).
  • The ability to isolate internal logic and form "clean" public APIs.

Trick questions.

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.

Common mistakes and anti-patterns

  • Poor organization of the file structure (lack of mod.rs, duplicate module declarations in different chunks).
  • Accessing struct fields without pub when a public API was planned.
  • Using pub everywhere without necessity, which increases the attack surface and reduces encapsulation.

Real-life example

Negative case

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:

  • Fast development at the start, less time spent on file organization.

Cons:

  • Dependency bloat appears, private details are accessible externally, hard to maintain the code.

Positive case

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:

  • Clean API, ease of maintenance, enhanced code security due to encapsulation.

Cons:

  • Requires initial planning of the structure, sometimes more code for exports.