|
5 | 5 | > We should discuss visibility, nesting, `mod.rs`, and any interesting patterns |
6 | 6 | > around modules. |
7 | 7 |
|
8 | | -## Basic design |
| 8 | +#### Naming conventions |
| 9 | +> **[OPEN]** |
| 10 | +> - Anything else? |
| 11 | +> - Are there cases where *not* separating words with underscores is OK, |
| 12 | +> or should this be a hard rule? |
9 | 13 |
|
10 | | -> **[OPEN]** This documents the simple, common pattern of module |
11 | | -> design - but others exist and improvements are appreciated. |
| 14 | +- Module names should contain only lowercae letters and underscores. |
| 15 | + For example, use `std::io::timer`, not `Std::IO::Timer`. |
| 16 | +- Multiple words should be separated by underscores. |
| 17 | + Use `std::local_data`, not `std::localData` or `std::localdata`. |
12 | 18 |
|
13 | | -The file `mod.rs` in a module defines the base-level imports of the |
14 | | -module. For all except trivial modules (and |
15 | | -[test cases](../testing/README.md)), it is better to keep this in a |
16 | | -separate file. |
| 19 | +#### Headers |
| 20 | +> **[OPEN]** Is this header organization suggestion valid? |
17 | 21 |
|
18 | | -A big use of `mod.rs` is to define a common interface for your module. The |
19 | | -internal structure can be whatever form that you might like, but then |
20 | | -this code will all get re-exported in `mod.rs` to the rest of the world. |
| 22 | +Organize module headers as follows: |
| 23 | + 1. [Imports](../style/imports.md). |
| 24 | + 1. `mod` declarations. |
| 25 | + 1. `pub mod` declarations. |
21 | 26 |
|
22 | | -This also serves a convenience purpose: users of your module only have |
23 | | -to remember the module name, and you can keep whatever internal |
24 | | -structure is required. |
| 27 | +#### Avoid `path` directives |
| 28 | +> **[OPEN]** This is hardly ever seen in the Rust codebase (only 4 uses, all in |
| 29 | +> `libsyntax`) and seems like overall a bad idea. |
25 | 30 |
|
26 | | -For example, say we had the following folder structure: |
| 31 | +Avoid using `#[path="..."]` directives except where it is *absolutely* |
| 32 | + necessary. |
27 | 33 |
|
28 | | -``` |
29 | | -myio/mod.rs |
30 | | - /mem.rs |
31 | | - /terminal/mod.rs |
32 | | -``` |
| 34 | +### Use the module hirearchy to organize APIs into coherent sections |
| 35 | +> **[OPEN]** |
33 | 36 |
|
34 | | -where we wish to keep `mem.rs` hidden from the outside world, and make |
35 | | -usage of `terminal` an explicit submodule. In `myio/mod.rs` we would |
36 | | -write: |
| 37 | +The module hirearchy defines both the public and internal API of your module. |
| 38 | +Breaking related functionality into submodules makes it understandable to both |
| 39 | +users and contributors to the module. |
37 | 40 |
|
38 | | -```rust |
39 | | -// myio/mod.rs |
| 41 | +#### Place modules in separate files |
| 42 | +> **[OPEN]** |
| 43 | +> - "<100 lines" is completely arbitrary, but it's a clearer recommendation |
| 44 | +> than "~1 page" or similar suggestions that vary by screen size, etc. |
40 | 45 |
|
41 | | -pub use self::mem::MemReader; |
| 46 | +For all except very short modules (<100 lines) and [tests](../testing/README.md), |
| 47 | +place the module `foo` in a separate file: either `foo.rs` or `foo/mod.rs`, |
| 48 | +depending on your needs, rather than declaring it inline like |
42 | 49 |
|
43 | | -mod mem; |
44 | | -pub mod terminal; |
| 50 | +```rust |
| 51 | +pub mod foo { |
| 52 | + pub fn bar() { println!("..."); } |
| 53 | + /* ... */ |
| 54 | +} |
45 | 55 | ``` |
46 | 56 |
|
47 | | -### Export common traits, structs, and enums at the module level |
48 | | - |
| 57 | +#### Use folders to organize submodules |
49 | 58 | > **[OPEN]** |
50 | 59 |
|
51 | | -In the above example, we re-export `MemReader`, but we might have others |
52 | | -that are common to the whole module, and not just `mem.rs`: |
| 60 | +For modules that themselves have submodules, place the module in a separate |
| 61 | +folder (e.g., `bar/mod.rs` for a module `bar`) rather than the same directory. |
53 | 62 |
|
54 | | -```rust |
55 | | -// myio/mod.rs |
| 63 | +Note the structure of |
| 64 | +[`std::io`](http://doc.rust-lang.org/std/io/). Many of the submodules lack |
| 65 | +children, like |
| 66 | +[`io::fs`](http://doc.rust-lang.org/std/io/fs/) |
| 67 | +and |
| 68 | +[`io::stdio`](http://doc.rust-lang.org/std/io/stdio/). |
| 69 | +On the other hand, |
| 70 | +[`io::net`](http://doc.rust-lang.org/std/io/net/) |
| 71 | +contains submodules, so it lives in a separate folder: |
56 | 72 |
|
57 | | -pub enum FileMode { /* ... */ } |
58 | | -pub trait Seek { /* ... */ } |
59 | | -pub struct File { /* ... */ } |
| 73 | +``` |
| 74 | +io/mod.rs |
| 75 | + io/extensions.rs |
| 76 | + io/fs.rs |
| 77 | + io/net/mod.rs |
| 78 | + io/net/addrinfo.rs |
| 79 | + io/net/ip.rs |
| 80 | + io/net/tcp.rs |
| 81 | + io/net/udp.rs |
| 82 | + io/net/unix.rs |
| 83 | + io/pipe.rs |
| 84 | + ... |
60 | 85 | ``` |
61 | 86 |
|
62 | | -Then, to use these common traits in submodules: |
63 | | - |
64 | | -```rust |
65 | | -// myio/mem.rs |
| 87 | +While it is possible to define all of `io` within a single folder, mirroring |
| 88 | +the module hirearchy in the directory structure makes submodules of `io::net` |
| 89 | +easier to find. |
66 | 90 |
|
67 | | -use super::Seek; |
| 91 | +#### Top-level definitions |
| 92 | +> **[OPEN]** |
68 | 93 |
|
69 | | -pub struct MemReader { /* ... */ } |
70 | | -impl MemReader { /* ... */ } |
71 | | -impl Seek for MemReader { /* ... */ } |
72 | | -``` |
| 94 | +Define or [reexport](http://doc.rust-lang.org/std/io/#reexports) commonly used |
| 95 | +definitions at the top level of your module. |
73 | 96 |
|
74 | | -Notice how both `Seek` and `MemReader` are both visible from |
75 | | -`myio::Seek` and `myio::MemReader`. |
| 97 | +Functionality that is related to the module itself should be defined in |
| 98 | +`mod.rs`, while functionality specific to a submodule should live in its |
| 99 | +related submodule and be reexported elsewhere. |
76 | 100 |
|
77 | | -### Use private modules to hide information |
| 101 | +For example, |
| 102 | +[`IoError`](http://doc.rust-lang.org/std/io/struct.IoError.html) |
| 103 | +is defined in `io/mod.rs`, since it pertains to the entirety of the submodule, |
| 104 | +while |
| 105 | +[`TcpStream`](http://doc.rust-lang.org/std/io/net/tcp/struct.TcpStream.html) |
| 106 | +is defined in `io/net/tcp.rs` and reexported in the `io` module. |
78 | 107 |
|
| 108 | +### Use internal module hirearchies for hiding implementations |
79 | 109 | > **[OPEN]** |
| 110 | +> - Referencing internal modules from the standard library is subject to |
| 111 | +> becoming outdated. |
| 112 | +
|
| 113 | +Internal module hirearchies (including private submodules) may be used to |
| 114 | +hide implementation details that are not part of the module's API. |
80 | 115 |
|
81 | | -This structure lets you achieve the goals of information hiding (the |
82 | | -implementation of `mem` is separate from the `MemReader` in our API) and |
83 | | -making all useful types available for the internal modules. |
| 116 | +For example, in [`std::io`](http://doc.rust-lang.org/std/io/), `mod mem` |
| 117 | +provides implementations for |
| 118 | +[`BufReader`](http://doc.rust-lang.org/std/io/struct.BufReader.html) |
| 119 | +and |
| 120 | +[`BufWriter`](http://doc.rust-lang.org/std/io/struct.BufWriter.html), |
| 121 | +but these are re-exported in `io/mod.rs` at the top level of the module: |
| 122 | + |
| 123 | +```rust |
| 124 | +// libstd/io/mod.rs |
| 125 | + |
| 126 | +pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter}; |
| 127 | +/* ... */ |
| 128 | +mod mem; |
| 129 | +``` |
84 | 130 |
|
85 | | -It is good practice to keep code that is likely to change hidden in this |
86 | | -manner, and only make public the parts that constitute the module's |
87 | | -interface. |
| 131 | +This hides the detail that there even exists a `mod mem` in `io`, and |
| 132 | +helps keep code organized while offering freedom to change the implementation. |
0 commit comments