From e21f883b032c8d7c6811cd2849eea7db4e72f21d Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 3 Jun 2015 16:26:47 -0700 Subject: [PATCH 1/3] rename attribute --- text/0000-rename.md | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 text/0000-rename.md diff --git a/text/0000-rename.md b/text/0000-rename.md new file mode 100644 index 00000000000..1d4079d7ccf --- /dev/null +++ b/text/0000-rename.md @@ -0,0 +1,58 @@ +- Feature Name: rename_attr +- Start Date: 2015-06-03 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Add a `#[renamed(from="old_name", since="...")]` attribute for renaming APIs in a forwards +*and* backwards compatible way that requires no effort or trouble to downstream consumers. + +# Motivation + +Naming things is hard, and APIs churned enough near the release of 1.0 that we stabilized on +some suboptimal names. We'll probably do it again in the future. Therefore it would be +desirable to deprecate these in favour of a rename. However to avoid breaking everyone +there needs to be some trick to make impls still work. There are a few ways to do this today, +but all of them cause janky behaviour. For instance consumers of one name might get different +behaviour. However if we could alias functions/methods under new names as in a first-class +way, we could avoid any problems. + +In addition, the mythical rustfix could identify these attributes and automatically fix the +source of anything using the old names. + +# Detailed design + +Add an attribute for functions `#[renamed(from="old_name", since="...")]`. This should have the following behaviour: + +* Any reference to the current or old_name should resolve to the current +* The old name can be silently shadowed, to avoid namespace pollution +* The function can only be implemented under one name (deny duplicates) +* Users of the old name should receive a `warn(renamed)` +* Multiple renames should be able to co-exist (though this would be really unfortunate) + +# Drawbacks + +There are none beyond the usual "adding stuff" + +# Alternatives + +Enable full `fn` aliases via a `fn foo = bar;` syntax. This would allow renaming +without any warning in the case that signatures match exactly but both names should +be exposed. For instance `fn new = Default::default` is a plausible thing to want. + +Then renames could theoretically be resolved simply by deprecating the alias. + +This design is not considered simply because it would be considerably more complex +and require substantially more debate to hammer out all the details (would we want +to support some kind of "currying" like `type` does with `type = Foo`?) + +The RFC author would like to have a tool for this in the short-term, as there are +several outstanding rename requests. + +# Unresolved questions + +* Should this be blocked on a more general design? +* Should this be enabled on traits and structs? They can just use `pub use`, but +they don't get all the other "niceness". Regardless this can be "upgraded" to later +support structs and traits. \ No newline at end of file From 5f7eadcedc3096f52ba1b7399d6f5ddff7638db0 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 3 Jun 2015 16:40:36 -0700 Subject: [PATCH 2/3] examples and cleanup --- text/0000-rename.md | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/text/0000-rename.md b/text/0000-rename.md index 1d4079d7ccf..ad99dcd9f40 100644 --- a/text/0000-rename.md +++ b/text/0000-rename.md @@ -14,12 +14,10 @@ Naming things is hard, and APIs churned enough near the release of 1.0 that we s some suboptimal names. We'll probably do it again in the future. Therefore it would be desirable to deprecate these in favour of a rename. However to avoid breaking everyone there needs to be some trick to make impls still work. There are a few ways to do this today, -but all of them cause janky behaviour. For instance consumers of one name might get different -behaviour. However if we could alias functions/methods under new names as in a first-class -way, we could avoid any problems. - -In addition, the mythical rustfix could identify these attributes and automatically fix the -source of anything using the old names. +but all of them cause janky behaviour for *trait methods*, because they have a diverse. +Set of implementors and default implementations. For instance consumers of one +name might get different behaviour. However if we could alias functions/methods under new names +in a first-class way, we could avoid any problems. # Detailed design @@ -31,6 +29,41 @@ Add an attribute for functions `#[renamed(from="old_name", since="...")]`. This * Users of the old name should receive a `warn(renamed)` * Multiple renames should be able to co-exist (though this would be really unfortunate) +In addition, the mythical rustfix could identify these attributes and automatically fix the +source of anything using the old names. + +## An example: + +We decide that `size_hint` should *really* have been `len_hint`. We go to the Iterator trait and +go from: + +```rust + /// Returns a lower and upper bound on the remaining length of the iterator. + /// + /// An upper bound of `None` means either there is no known upper bound, or + /// the upper bound does not fit within a `usize`. + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + fn size_hint(&self) -> (usize, Option) { (0, None) } +``` + +to + +```rust + /// Returns a lower and upper bound on the remaining length of the iterator. + /// + /// An upper bound of `None` means either there is no known upper bound, or + /// the upper bound does not fit within a `usize`. + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[renamed(from = "size_hint", since = "1.2.0")] + fn len_hint(&self) -> (usize, Option) { (0, None) } +``` + +Now everyone who implements or consumes `size_hint` receives a `warn(renamed)`. +A trivial grep party (or ideally rustfix), updates everything to use `len_hint`, +and the warnings go away. + # Drawbacks There are none beyond the usual "adding stuff" From efe736968b0fae562fddd12091e4825849634cbe Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Tue, 30 Jun 2015 18:56:08 -0700 Subject: [PATCH 3/3] complete rewrite to use use --- text/0000-rename.md | 211 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 43 deletions(-) diff --git a/text/0000-rename.md b/text/0000-rename.md index ad99dcd9f40..f1d2fb987b6 100644 --- a/text/0000-rename.md +++ b/text/0000-rename.md @@ -5,22 +5,144 @@ # Summary -Add a `#[renamed(from="old_name", since="...")]` attribute for renaming APIs in a forwards -*and* backwards compatible way that requires no effort or trouble to downstream consumers. +Extend `use` to work in trait definitions and impl blocks for re-exporting associated +items under different names. This provides a clean way to rename items harmlessly, and +provides a more ergonomic way to provide the same functionality under different names +when necessary to satisfy an API. + +This enables + +```rust +trait Iterator { + fn len_hint(&self) -> (usize, Option) { + (0, None) + } + + #[deprecated("renamed to len_hint")] + use Self::len_hint as size_hint; +} +``` + + # Motivation Naming things is hard, and APIs churned enough near the release of 1.0 that we stabilized on some suboptimal names. We'll probably do it again in the future. Therefore it would be -desirable to deprecate these in favour of a rename. However to avoid breaking everyone -there needs to be some trick to make impls still work. There are a few ways to do this today, -but all of them cause janky behaviour for *trait methods*, because they have a diverse. -Set of implementors and default implementations. For instance consumers of one -name might get different behaviour. However if we could alias functions/methods under new names -in a first-class way, we could avoid any problems. +desirable to deprecate these in favour of a rename. It's currently possible to do this for +top level items like structs, traits, and functions as follows: + +```rust +#[deprecated(reason = "renamed to new struct")] +pub use NewStruct as OldStruct; + +// Note: this was originally called OldStruct +struct NewStruct { + ... +} +``` + +However it is not possible to do this for scoped items like methods (inherent or trait). +For inherent implementations this isn't a particularly big deal, just slightly less +ergonomic. However for trait methods this can be particularly nasty. For instance, +consider size_hint on Iterator: + +```rust +trait Iterator { + fn size_hint(&self) -> (usize, Option) { + (0, None) + } +} +``` + +If we want to rename size_hint to len_hint, we could do the following: + +```rust +trait Iterator { + #[deprecated(reason = "renamed to len_hint")] + fn size_hint(&self) -> (usize, Option) { + (0, None) + } + + #[allow(deprecated)] + fn len_hint(&self) -> (usize, Option) { + self.size_hint() + } +} +``` + +However this is *backwards*. In particular, everyone in the world *wants* to +just implement len_hint since size_hint is deprecated, but the "most effecient" +thing to do is implement size_hint. If you *do* just implement len_hint, then +you have the unfortunate situation that *size_hint is still implemented, but +with a different implementation! + +If we instead do the reverse: + +```rust +trait Iterator { + #[deprecated(reason = "renamed to len_hint")] + fn size_hint(&self) -> (usize, Option) { + self.len_hint() + } + + fn len_hint(&self) -> (usize, Option) { + (0, None) + } +} +``` + +Then legacy implementors of size_hint will fail to provide len_hint, which is what +everyone should be using! + +What we would like to do is explicitly state that these two methods are *one and the +same*, and that implementing one is implementing the other. Implementing *both* in +particular should be illegal. Like with structs, we would like to do the following: + +```rust +trait Iterator { + fn len_hint(&self) -> (usize, Option) { + (0, None) + } + + #[deprecated(reason = "renamed to len_hint")] + use Self::len_hint as size_hint; +} +``` + +All legacy implementors of size_hint would be correctly forwarded to len_hint, and +all conformant implementors of len_hint would be silently forwarded to legacy consumers +of size_hint. In addition, any attempt to implement both would be met with a duplicate +implementation error (as implementing *any* method twice would). + + # Detailed design +Allow `use` to be placed in trait definition and impl blocks to re-export arbitrary +associated items under a different name. In traits declarations, this would enable +a trait to be implemented by providing an item under *either* name, while specifically +marking one name as deprecated. In inherent impl blocks, this would enable providing +the same method under different names. + +This RFC does not currently propose allowing traits to be implemented by using +a method with the appropriate name. It also does not propose allowing names to be +obtained from other impl blocks. This is to minimize complexity, although the +author's knowledge of the ever-cryptic *resolve* subsystem is sufficiently limited +that it may be *simpler* to allow these things. See the unresolved questions +section for details. + + +# Drawbacks + +This could further complicate the already over-burdened *resolve* system. It may +also be confusing/surprising to see `use` used in this manner. + + + +# Alternatives + + Add an attribute for functions `#[renamed(from="old_name", since="...")]`. This should have the following behaviour: * Any reference to the current or old_name should resolve to the current @@ -30,24 +152,8 @@ Add an attribute for functions `#[renamed(from="old_name", since="...")]`. This * Multiple renames should be able to co-exist (though this would be really unfortunate) In addition, the mythical rustfix could identify these attributes and automatically fix the -source of anything using the old names. - -## An example: - -We decide that `size_hint` should *really* have been `len_hint`. We go to the Iterator trait and -go from: - -```rust - /// Returns a lower and upper bound on the remaining length of the iterator. - /// - /// An upper bound of `None` means either there is no known upper bound, or - /// the upper bound does not fit within a `usize`. - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - fn size_hint(&self) -> (usize, Option) { (0, None) } -``` - -to +source of anything using the old names. The len_hint example would then be resolved as +follows. ```rust /// Returns a lower and upper bound on the remaining length of the iterator. @@ -64,28 +170,47 @@ Now everyone who implements or consumes `size_hint` receives a `warn(renamed)`. A trivial grep party (or ideally rustfix), updates everything to use `len_hint`, and the warnings go away. -# Drawbacks +This design was originally proposed by this RFC, but was generally frowned upon +as "too specific". -There are none beyond the usual "adding stuff" -# Alternatives -Enable full `fn` aliases via a `fn foo = bar;` syntax. This would allow renaming -without any warning in the case that signatures match exactly but both names should -be exposed. For instance `fn new = Default::default` is a plausible thing to want. +# Unresolved questions -Then renames could theoretically be resolved simply by deprecating the alias. +Should it be possible to import items from different blocks? That is, should it be +possible to do: -This design is not considered simply because it would be considerably more complex -and require substantially more debate to hammer out all the details (would we want -to support some kind of "currying" like `type` does with `type = Foo`?) +```rust +impl Default for Vec { + fn default() -> Self { .. } +} + +impl Vec { + pub use Self::default as new; +} +``` -The RFC author would like to have a tool for this in the short-term, as there are -several outstanding rename requests. +or -# Unresolved questions -* Should this be blocked on a more general design? -* Should this be enabled on traits and structs? They can just use `pub use`, but -they don't get all the other "niceness". Regardless this can be "upgraded" to later -support structs and traits. \ No newline at end of file +```rust +impl Foo { + fn foo(&self) { .. } +} + +impl Foo { + pub use Self::foo as bar; +} +``` + + +----- + +Should it be possible to implement a trait by just using items from elsewhere that +have the right signature? e.g. + +``` +impl Default for Vec { + pub use Self::new as default; +} +``` \ No newline at end of file