From a23a5c9fc51cabc478e6ae62da084446a1410533 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 17 Feb 2016 00:50:23 +0000 Subject: [PATCH] Start writing the nameable_interfaces RFC --- text/0000-nameable-interfaces.md | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 text/0000-nameable-interfaces.md diff --git a/text/0000-nameable-interfaces.md b/text/0000-nameable-interfaces.md new file mode 100644 index 00000000000..944ffa95843 --- /dev/null +++ b/text/0000-nameable-interfaces.md @@ -0,0 +1,116 @@ +- Feature Name: nameable_interfaces +- Start Date: 2016-2-10 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +This RFC requires that an item which is nameable from a given module have an interface +that is nameable from the module. + +An item is "nameable" from a module if there is a path in the module's scope that resolves +to the item, possibly through a reexport. +By the "interface" of an item, I am referring to the "public-facing parts" of that item as defined +in [RFC #0136](https://github.com/rust-lang/rfcs/pull/0136), +i.e. the parts of the item that the RFC requires to be public whenever the item is public. + +# Motivation +[motivation]: #motivation + +The primary motivation for nameable interfaces is refactorability. +Nameable interfaces ensure that the type of any expression is nameable, guaranteeing that many +common types of refactoring are always possible, including + - type annotating a let declaration, + - refactoring an expression without early returns into a function item, + - refactoring a closure without upvars into a function item, and + - refactoring a constant expression into a constant item. + +Another motivation is documentability. This RFC would ensure that rustdoc never has to include +an inaccessible path in the documentation for a crate. + +# Detailed design +[design]: #detailed-design + +TODO describe design in more detail + +Examples: +```rust +mod foo { + mod bar { + pub struct Bar; + } + + pub fn f() -> bar::Bar {} + //~^ ERROR/WARN `f` is visible outside `foo`, but `bar::Bar` is not. + //~| NOTE consider either not declaring `f` with `pub` or reexporting `bar::Bar`. +} + +mod foo { + mod bar { + pub struct Bar; + } + + mod baz { + pub fn f() -> bar::Bar {} + //~^ ERROR/WARN `f` is visible outside `foo`, but `bar::Bar` is not. + } + + pub use self::baz::f; + //~^ NOTE consider either not reexporting `f` or also reexporing `bar::Bar`. +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +One drawback is the hassle for developers who have to fix unnameable interfaces or face warnings and +eventually breakage (unless we keep it a warning indefinitely). +One way to ameliorate this is to have clear warnings that suggest adding specific reexports +that would bring the code up to compliance. + + +The other main drawback is that adding the suggested reexports can sometimes expose previously +private implementation details. More specifically, + - reexporting a struct or an enum can allow access to its inherent methods, + - reexporting a struct can allow access to its constructor and fields, + - reexporting a trait allows access to its methods, and + - reexporting an enum allows access to its variants and their fields. + +[RFC #1422](https://github.com/rust-lang/rfcs/pull/1422) will address the first two points by +allowing developers to rehide any exposed inherent methods, struct constructors, or struct fields. + +More specifically, once can rehide inherent methods and struct fields by declaring them with the +pub(restricted) syntax to restrict their visibility to where the corresponding struct or enum was +nameable. +For structs with fields, rehiding the fields will also rehide the constructor. +For structs without fields, one must add a pub(restricted) dummy field to rehide the constructor. + +To rehide enum variants and trait methods, RFC #1422 will have to be extended to allow the +pub(restricted) syntax on trait methods, enum variants, and the variants' fields. I think this +extension would be a good idea anyway. + +# Alternatives +[alternatives]: #alternatives + +We could choose not to require that interfaces be nameable. + +# Unresolved questions +[unresolved]: #unresolved-questions + +We could make nameable interfaces not only necessary but also sufficient to pass the +`private_in_public` check in `librustc_privacy`. This would allow, for example, +```rust +mod foo { + struct Bar; // `Bar` is nameable inside `foo` + mod baz { + pub fn f() -> Bar {} // Since f is not nameable outside `foo`, this use of `Bar` is ok. + } +} +``` + +This addition would make nameability synonymous with visibility, which seems to be what most people +expect (as evidenced by the discussions in rust issues +[#18082](https://github.com/rust-lang/rust/issues/18082), +[#23585](https://github.com/rust-lang/rust/issues/23585), and +[#30905](https://github.com/rust-lang/rust/issues/30905)).