% Modules
[OPEN] What general guidelines should we provide for module design?
We should discuss visibility, nesting,
mod.rs
, and any interesting patterns around modules.
Organize module headers as follows:
- Imports.
mod
declarations.pub mod
declarations.
Avoid using #[path="..."]
directives; make the file system and
module hierarchy match, instead.
[FIXME] Flesh this out with examples; explain what a "coherent section" is with examples.
The module hirearchy defines both the public and internal API of your module. Breaking related functionality into submodules makes it understandable to both users and contributors to the module.
[OPEN]
- "<100 lines" is arbitrary, but it's a clearer recommendation than "~1 page" or similar suggestions that vary by screen size, etc.
For all except very short modules (<100 lines) and tests,
place the module foo
in a separate file, as in:
pub mod foo;
// in foo.rs or foo/mod.rs
pub fn bar() { println!("..."); }
/* ... */
rather than declaring it inline:
pub mod foo {
pub fn bar() { println!("..."); }
/* ... */
}
For modules that themselves have submodules, place the module in a separate
directory (e.g., bar/mod.rs
for a module bar
) rather than the same directory.
Note the structure of
std::io
. Many of the submodules lack
children, like
io::fs
and
io::stdio
.
On the other hand,
io::net
contains submodules, so it lives in a separate directory:
io/mod.rs
io/extensions.rs
io/fs.rs
io/net/mod.rs
io/net/addrinfo.rs
io/net/ip.rs
io/net/tcp.rs
io/net/udp.rs
io/net/unix.rs
io/pipe.rs
...
While it is possible to define all of io
within a single directory,
mirroring the module hirearchy in the directory structure makes
submodules of io::net
easier to find.
For modules with submodules, define or reexport commonly used definitions at the top level:
- Functionality relevant to the module itself or to many of its
children should be defined in
mod.rs
. - Functionality specific to a submodule should live in that submodule. Reexport at the top level for the most important or common definitions.
For example,
IoError
is defined in io/mod.rs
, since it pertains to the entirety of io
,
while
TcpStream
is defined in io/net/tcp.rs
and reexported in the io
module.
[OPEN]
- Referencing internal modules from the standard library is subject to becoming outdated.
Internal module hirearchies (i.e., private submodules) may be used to hide implementation details that are not part of the module's API.
For example, in std::io
, mod mem
provides implementations for
BufReader
and
BufWriter
,
but these are re-exported in io/mod.rs
at the top level of the module:
// libstd/io/mod.rs
pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter};
/* ... */
mod mem;
This hides the detail that there even exists a mod mem
in io
, and
helps keep code organized while offering freedom to change the
implementation.