|
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