|
| 1 | +- Feature Name: nameable_interfaces |
| 2 | +- Start Date: 2016-2-10 |
| 3 | +- RFC PR: |
| 4 | +- Rust Issue: |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +This RFC requires that an item which is nameable from a given module have an interface |
| 10 | +that is nameable from the module. |
| 11 | + |
| 12 | +An item is "nameable" from a module if there is a path in the module's scope that resolves |
| 13 | +to the item, possibly through a reexport. |
| 14 | +By the "interface" of an item, I am referring to the "public-facing parts" of that item as defined |
| 15 | +in [RFC #0136](https://github.com/rust-lang/rfcs/pull/0136), |
| 16 | +i.e. the parts of the item that the RFC requires to be public whenever the item is public. |
| 17 | + |
| 18 | +# Motivation |
| 19 | +[motivation]: #motivation |
| 20 | + |
| 21 | +The primary motivation for nameable interfaces is refactorability. |
| 22 | +Nameable interfaces ensure that the type of any expression is nameable, guaranteeing that many |
| 23 | +common types of refactoring are always possible, including |
| 24 | + - type annotating a let declaration, |
| 25 | + - refactoring an expression without early returns into a function item, |
| 26 | + - refactoring a closure without upvars into a function item, and |
| 27 | + - refactoring a constant expression into a constant item. |
| 28 | + |
| 29 | +Another motivation is documentability. This RFC would ensure that rustdoc never has to include |
| 30 | +an inaccessible path in the documentation for a crate. |
| 31 | + |
| 32 | +# Detailed design |
| 33 | +[design]: #detailed-design |
| 34 | + |
| 35 | +TODO describe design in more detail |
| 36 | + |
| 37 | +Examples: |
| 38 | +```rust |
| 39 | +mod foo { |
| 40 | + mod bar { |
| 41 | + pub struct Bar; |
| 42 | + } |
| 43 | + |
| 44 | + pub fn f() -> bar::Bar {} |
| 45 | + //~^ ERROR/WARN `f` is visible outside `foo`, but `bar::Bar` is not. |
| 46 | + //~| NOTE consider either not declaring `f` with `pub` or reexporting `bar::Bar`. |
| 47 | +} |
| 48 | + |
| 49 | +mod foo { |
| 50 | + mod bar { |
| 51 | + pub struct Bar; |
| 52 | + } |
| 53 | + |
| 54 | + mod baz { |
| 55 | + pub fn f() -> bar::Bar {} |
| 56 | + //~^ ERROR/WARN `f` is visible outside `foo`, but `bar::Bar` is not. |
| 57 | + } |
| 58 | + |
| 59 | + pub use self::baz::f; |
| 60 | + //~^ NOTE consider either not reexporting `f` or also reexporing `bar::Bar`. |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +# Drawbacks |
| 65 | +[drawbacks]: #drawbacks |
| 66 | + |
| 67 | +One drawback is the hassle for developers who have to fix unnameable interfaces or face warnings and |
| 68 | +eventually breakage (unless we keep it a warning indefinitely). |
| 69 | +One way to ameliorate this is to have clear warnings that suggest adding specific reexports |
| 70 | +that would bring the code up to compliance. |
| 71 | + |
| 72 | + |
| 73 | +The other main drawback is that adding the suggested reexports can sometimes expose previously |
| 74 | +private implementation details. More specifically, |
| 75 | + - reexporting a struct or an enum can allow access to its inherent methods, |
| 76 | + - reexporting a struct can allow access to its constructor and fields, |
| 77 | + - reexporting a trait allows access to its methods, and |
| 78 | + - reexporting an enum allows access to its variants and their fields. |
| 79 | + |
| 80 | +[RFC #1422](https://github.com/rust-lang/rfcs/pull/1422) will address the first two points by |
| 81 | +allowing developers to rehide any exposed inherent methods, struct constructors, or struct fields. |
| 82 | + |
| 83 | +More specifically, once can rehide inherent methods and struct fields by declaring them with the |
| 84 | +pub(restricted) syntax to restrict their visibility to where the corresponding struct or enum was |
| 85 | +nameable. |
| 86 | +For structs with fields, rehiding the fields will also rehide the constructor. |
| 87 | +For structs without fields, one must add a pub(restricted) dummy field to rehide the constructor. |
| 88 | + |
| 89 | +To rehide enum variants and trait methods, RFC #1422 will have to be extended to allow the |
| 90 | +pub(restricted) syntax on trait methods, enum variants, and the variants' fields. I think this |
| 91 | +extension would be a good idea anyway. |
| 92 | + |
| 93 | +# Alternatives |
| 94 | +[alternatives]: #alternatives |
| 95 | + |
| 96 | +We could choose not to require that interfaces be nameable. |
| 97 | + |
| 98 | +# Unresolved questions |
| 99 | +[unresolved]: #unresolved-questions |
| 100 | + |
| 101 | +We could make nameable interfaces not only necessary but also sufficient to pass the |
| 102 | +`private_in_public` check in `librustc_privacy`. This would allow, for example, |
| 103 | +```rust |
| 104 | +mod foo { |
| 105 | + struct Bar; // `Bar` is nameable inside `foo` |
| 106 | + mod baz { |
| 107 | + pub fn f() -> Bar {} // Since f is not nameable outside `foo`, this use of `Bar` is ok. |
| 108 | + } |
| 109 | +} |
| 110 | +``` |
| 111 | + |
| 112 | +This addition would make nameability synonymous with visibility, which seems to be what most people |
| 113 | +expect (as evidenced by the discussions in rust issues |
| 114 | +[#18082](https://github.com/rust-lang/rust/issues/18082), |
| 115 | +[#23585](https://github.com/rust-lang/rust/issues/23585), and |
| 116 | +[#30905](https://github.com/rust-lang/rust/issues/30905)). |
0 commit comments