Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

features/modules: clarification & updates

Per Aaron Turon's suggestions, this clarifies much of the wording for
modules.

Additionally, it adds recommendations about:

- Module naming
- Header ordering
- path directives
  • Loading branch information
nathantypanski committed Jul 2, 2014
1 parent bdbf206 commit 4c208773d48d7512302a0cf01380401edceea354
Showing with 99 additions and 54 deletions.
  1. +99 −54 features/modules.md
@@ -5,83 +5,128 @@
> We should discuss visibility, nesting, `mod.rs`, and any interesting patterns
> around modules.
## Basic design
#### Naming conventions
> **[OPEN]**
> - Anything else?
> - Are there cases where *not* separating words with underscores is OK,
> or should this be a hard rule?
> **[OPEN]** This documents the simple, common pattern of module
> design - but others exist and improvements are appreciated.
- Module names should contain only lowercae letters and underscores.
For example, use `std::io::timer`, not `Std::IO::Timer`.
- Multiple words should be separated by underscores.
Use `std::local_data`, not `std::localData` or `std::localdata`.

The file `mod.rs` in a module defines the base-level imports of the
module. For all except trivial modules (and
[test cases](../testing/README.md)), it is better to keep this in a
separate file.
#### Headers
> **[OPEN]** Is this header organization suggestion valid?
A big use of `mod.rs` is to define a common interface for your module. The
internal structure can be whatever form that you might like, but then
this code will all get re-exported in `mod.rs` to the rest of the world.
Organize module headers as follows:
1. [Imports](../style/imports.md).
1. `mod` declarations.
1. `pub mod` declarations.

This also serves a convenience purpose: users of your module only have
to remember the module name, and you can keep whatever internal
structure is required.
#### Avoid `path` directives
> **[OPEN]** This is hardly ever seen in the Rust codebase (only 4 uses, all in
> `libsyntax`) and seems like overall a bad idea.
For example, say we had the following folder structure:
Avoid using `#[path="..."]` directives except where it is *absolutely*
necessary.

```
myio/mod.rs
/mem.rs
/terminal/mod.rs
```
### Use the module hirearchy to organize APIs into coherent sections
> **[OPEN]**
where we wish to keep `mem.rs` hidden from the outside world, and make
usage of `terminal` an explicit submodule. In `myio/mod.rs` we would
write:
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.

```rust
// myio/mod.rs
#### Place modules in separate files
> **[OPEN]**
> - "<100 lines" is completely arbitrary, but it's a clearer recommendation
> than "~1 page" or similar suggestions that vary by screen size, etc.
pub use self::mem::MemReader;
For all except very short modules (<100 lines) and [tests](../testing/README.md),
place the module `foo` in a separate file: either `foo.rs` or `foo/mod.rs`,
depending on your needs, rather than declaring it inline like

mod mem;
pub mod terminal;
```rust
pub mod foo {
pub fn bar() { println!("..."); }
/* ... */
}
```

### Export common traits, structs, and enums at the module level

#### Use folders to organize submodules
> **[OPEN]**
In the above example, we re-export `MemReader`, but we might have others
that are common to the whole module, and not just `mem.rs`:
For modules that themselves have submodules, place the module in a separate
folder (e.g., `bar/mod.rs` for a module `bar`) rather than the same directory.

```rust
// myio/mod.rs
Note the structure of
[`std::io`](http://doc.rust-lang.org/std/io/). Many of the submodules lack
children, like
[`io::fs`](http://doc.rust-lang.org/std/io/fs/)
and
[`io::stdio`](http://doc.rust-lang.org/std/io/stdio/).
On the other hand,
[`io::net`](http://doc.rust-lang.org/std/io/net/)
contains submodules, so it lives in a separate folder:

pub enum FileMode { /* ... */ }
pub trait Seek { /* ... */ }
pub struct File { /* ... */ }
```
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
...
```

Then, to use these common traits in submodules:

```rust
// myio/mem.rs
While it is possible to define all of `io` within a single folder, mirroring
the module hirearchy in the directory structure makes submodules of `io::net`
easier to find.

use super::Seek;
#### Top-level definitions
> **[OPEN]**
pub struct MemReader { /* ... */ }
impl MemReader { /* ... */ }
impl Seek for MemReader { /* ... */ }
```
Define or [reexport](http://doc.rust-lang.org/std/io/#reexports) commonly used
definitions at the top level of your module.

Notice how both `Seek` and `MemReader` are both visible from
`myio::Seek` and `myio::MemReader`.
Functionality that is related to the module itself should be defined in
`mod.rs`, while functionality specific to a submodule should live in its
related submodule and be reexported elsewhere.

### Use private modules to hide information
For example,
[`IoError`](http://doc.rust-lang.org/std/io/struct.IoError.html)
is defined in `io/mod.rs`, since it pertains to the entirety of the submodule,
while
[`TcpStream`](http://doc.rust-lang.org/std/io/net/tcp/struct.TcpStream.html)
is defined in `io/net/tcp.rs` and reexported in the `io` module.

### Use internal module hirearchies for hiding implementations
> **[OPEN]**
> - Referencing internal modules from the standard library is subject to
> becoming outdated.
Internal module hirearchies (including private submodules) may be used to
hide implementation details that are not part of the module's API.

This structure lets you achieve the goals of information hiding (the
implementation of `mem` is separate from the `MemReader` in our API) and
making all useful types available for the internal modules.
For example, in [`std::io`](http://doc.rust-lang.org/std/io/), `mod mem`
provides implementations for
[`BufReader`](http://doc.rust-lang.org/std/io/struct.BufReader.html)
and
[`BufWriter`](http://doc.rust-lang.org/std/io/struct.BufWriter.html),
but these are re-exported in `io/mod.rs` at the top level of the module:

```rust
// libstd/io/mod.rs
pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter};
/* ... */
mod mem;
```

It is good practice to keep code that is likely to change hidden in this
manner, and only make public the parts that constitute the module's
interface.
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.

0 comments on commit 4c20877

Please sign in to comment.
You can’t perform that action at this time.