|
| 1 | +- Start Date: 2014-03-23 |
| 2 | +- RFC PR #: |
| 3 | +- Rust Issue #: |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +A proposal for making the `mod` keyword do what everybody expects, and make |
| 8 | +it easier to manage multiple modules in a directory. The actual semantics of |
| 9 | +the `use` keyword would not significantly change, but the practical use |
| 10 | +would be tightened. |
| 11 | + |
| 12 | +# Motivation |
| 13 | + |
| 14 | +Note: in this proposal, I omit most use of the `pub` keyword, even if is |
| 15 | +required. When I speak of "public", I refer only to conceptual visibility. |
| 16 | + |
| 17 | +Frequently, newcomers to the language try to put a `mod` statement in |
| 18 | +every file that accesses a module. Partilarly, they expect `mod` to work |
| 19 | +like C/C++ `#include` or python `import`. C++ `using` or python `from x import` |
| 20 | +only do a single duty of bringing a name into scope, but Rust's `use` does |
| 21 | +the equivalent of both `#include` and `using`, and there is no equivalent |
| 22 | +to Rust's `mod` at all. |
| 23 | + |
| 24 | +Being confusing to newcomers is, in itself, a bug. But the strange purpose |
| 25 | +of Rust's `mod` is also harmful even if you know what it does. Consider the |
| 26 | +case when you have many files in a module. Several of the files in this |
| 27 | +module require some shared routines. Currently, the mod.rs is required |
| 28 | +to not only list the `mod` statements for the "public" modules (which is |
| 29 | +reasonable, though some might argue in favor of `mod *;` to catch them all), |
| 30 | +but also all of the private details that those modules use. |
| 31 | + |
| 32 | +Apparently, the current way is not confusing if coming from Javascript, |
| 33 | +but Javascript is an example of good *marketing*, not good *design*. |
| 34 | + |
| 35 | +My proposal is mostly based on the way Python does it. |
| 36 | + |
| 37 | +# Detailed design |
| 38 | + |
| 39 | +## Source layout |
| 40 | + |
| 41 | +- src/main.rs |
| 42 | + `mod fish;` |
| 43 | + `mod bird;` |
| 44 | + `mod penguin;` |
| 45 | +- src/fish.rs |
| 46 | +- src/bird.rs |
| 47 | + `mod penguin;` |
| 48 | +- src/penguin/ |
| 49 | + - src/penguin/mod.rs |
| 50 | + `mod foo;` |
| 51 | + `mod bar;` |
| 52 | + `use self::foo::popular_function;` |
| 53 | + `use super::bird;` |
| 54 | + `use ::fish::swim;` |
| 55 | + - src/penguin/foo.rs |
| 56 | + `mod bar;` |
| 57 | + `mod detail;` |
| 58 | + - src/penguin/bar.rs |
| 59 | + `mod detail;` |
| 60 | + - src/penguin/detail.rs |
| 61 | + `use self::bar;` (\*) |
| 62 | + `use super::fish;` (\*) |
| 63 | + |
| 64 | +(Those (\*)s would need an extra super:: in alternative #3.) |
| 65 | + |
| 66 | +## Stop |
| 67 | + |
| 68 | +Go back and look at the source layout again. |
| 69 | + |
| 70 | +I bet you (whether familiar with Rust, or only with other languages) |
| 71 | +understood *exactly* what that source layout means without having to read |
| 72 | +any documentation at all. Documentation is *never* a substitute for being |
| 73 | +obvious. |
| 74 | + |
| 75 | +## Suggested Approach |
| 76 | + |
| 77 | +First, introduce the (transparent) concept of a 'package'. A package is a |
| 78 | +collection of modules from one directory. A crate has a root package. |
| 79 | +A package may contain subpackages, which are also modules. |
| 80 | + |
| 81 | +One of the modules in a package is considered the root module. For the |
| 82 | +root package, this is main.rs or lib.rs; for subpackages this is mod.rs. |
| 83 | +All of the visible names in the root module are also visible when the |
| 84 | +package is viewed as a module. Besides the obvious subpackage case, this |
| 85 | +is important for locating `fn main`, and can also happen with `super::`. |
| 86 | + |
| 87 | +To avoid cycles during compilation, the modules may be completely loaded, |
| 88 | +or just registered to be loaded when done with the current load |
| 89 | +(alternatively, the "return early" method may be used). |
| 90 | + |
| 91 | +When actually loading a file, if the compiler see `mod foo` at any point - |
| 92 | +even `mod bar { mod foo; }` - it tries to add foo from the current |
| 93 | +directory. If foo/mod.rs exists, it is added as a new package (just like |
| 94 | +the root package was), otherwise it is added as a module. |
| 95 | + |
| 96 | +Note that any modules in a package that are not directly visible from its |
| 97 | +root module will not be directly visible from the package when the package |
| 98 | +is used as a module. With the example layout, `::penguin::detail` does not |
| 99 | +exist (but `::penguin::foo::detail` and `::penguin::bar::detail` do - if they |
| 100 | +didn't `pub` mod/use it, this has the benefits of Java's package-private). |
| 101 | + |
| 102 | +# Alternatives |
| 103 | + |
| 104 | +- Do nothing. Insist to the newcomers "it'll be less confusing later". |
| 105 | +- Just eliminate `mod` entirely. Automatically find the right filename |
| 106 | + when `use` is used (this is basically how Java imports work). |
| 107 | +- Fix the meaning of `mod`, but don't think about 'package'. This could |
| 108 | + work, but has a slightly pointless meaning for `self::` and `super::` |
| 109 | + |
| 110 | +# Unresolved questions |
| 111 | + |
| 112 | +All of these questions can be put off until after this RFC is implemented, |
| 113 | +though due attention should be given to the first. |
| 114 | + |
| 115 | +- Should `use foo::...` default to `use self::foo::...` or `use ::foo::...` |
| 116 | + I'm fond of defaulting `self::`, but rust currently does `::`. |
| 117 | + Perhaps it should be forbidden entirely (or gated) for a while? |
| 118 | +- Are there any additional improvements that could be made for multiple |
| 119 | + crates? (make another RFC after this one is implemented). |
| 120 | +- Should `mod *;` be added so a root module need not list all the .rs files? |
| 121 | +- Should there be a way to not have a mod.rs at all? |
| 122 | +- Should `mod foo { }` be feature-gated? |
0 commit comments