From 7f14018bdac6ce02abef15533329845d05905702 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 5 Nov 2014 11:12:43 -0800 Subject: [PATCH 1/2] RFC: extension trait conventions --- text/0000-extension-trait-conventions.md | 139 +++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 text/0000-extension-trait-conventions.md diff --git a/text/0000-extension-trait-conventions.md b/text/0000-extension-trait-conventions.md new file mode 100644 index 00000000000..431392f7ccb --- /dev/null +++ b/text/0000-extension-trait-conventions.md @@ -0,0 +1,139 @@ +- Start Date: 2014-11-05 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +This is a conventions RFC establishing a definition and naming +convention for *extension traits*: `FooExt`. + +# Motivation + +This RFC is part of the ongoing API conventions and stabilization +effort. + +Extension traits are a programming pattern that makes it +possible to add methods to an existing type outside of the crate +defining that type. While they should be used sparingly, the new +[object safety rules](https://github.com/rust-lang/rfcs/pull/255) have +increased the need for this kind of trait, and hence the need for a +clear convention. + +# Detailed design + +## What is an extension trait? + +Rust currently allows inherent methods to be defined on a type only in +the crate where that type is defined. But it is often the case that +clients of a type would like to incorporate additional methods to +it. Extension traits are a pattern for doing so: + +```rust +extern crate foo; +use foo::Foo; + +trait FooExt { + fn bar(&self); +} + +impl FooExt for Foo { + fn bar(&self) { .. } +} +``` + +By defining a new trait, a client of `foo` can add new methods to `Foo`. + +Of course, adding methods via a new trait happens all the time. What +makes it an *extension* trait is that the trait is not designed for +*generic* use, but only as way of adding methods to a specific type or +family of types. + +This is of course a somewhat subjective distinction. Whenever +designing an extension trait, one should consider whether the trait +could be used in some more generic way. If so, the trait should be +named and exported as if it were just a "normal" trait. But traits +offering groups of methods that really only make sense in the context +of some particular type(s) are true extension traits. + +The new +[object safety rules](https://github.com/rust-lang/rfcs/pull/255) mean +that a trait can only be used for trait objects if *all* of its +methods are usable; put differently, it ensures that for "object safe +traits" there is always a canonical way to implement `Trait` for +`Box`. To deal with this new rule, it is sometimes necessary to +break traits apart into an object safe trait and extension traits: + +```rust +// The core, object-safe trait +trait Iterator { + fn next(&mut self) -> Option; +} + +// The extension trait offering object-unsafe methods +trait IteratorExt: Iterator { + fn chain>(self, other: U) -> Chain { ... } + fn zip>(self, other: U) -> Zip { ... } + fn map(self, f: |A| -> B) -> Map<'r, A, B, Self> { ... } + ... +} + +// A blanket impl +impl IteratorExt for I where I: Iterator { + ... +} +``` + +Note that, although this split-up definition is somewhat more complex, +it is also more flexible: because `Box>` will implement +`Iterator`, you can now use *all* of the adapter methods provided +in `IteratorExt` on trait objects, even though they are not object +safe. + +## The convention + +The proposed convention is, first of all, to (1) prefer adding default +methods to existing traits or (2) prefer generically useful traits to +extension traits whenever feasible. + +For true extension traits, there should be a clear type or trait that +they are extending. The extension trait should be called `FooExt` +where `Foo` is that type or trait. + +In some cases, the extension trait only applies conditionally. For +example, `AdditiveIterator` is an extension trait currently in `std` +that applies to iterators over numeric types. These extension traits +should follow a similar convention, putting together the type/trait +name and the qualifications, together with the `Ext` suffix: +`IteratorAddExt`. + +## Future proofing + +In the future, the need for many of these extension traits may +disappear as other languages features are added. For example, +method-level `where` clauses will eliminate the need for +`AdditiveIterator`. And allowing inherent `impl`s like `impl +T { .. }` for the crate defining `Trait` would eliminate even more. + +However, there will always be *some* use of extension traits, and we +need to stabilize the 1.0 libraries prior to these language features +landing. So this is the proposed convention for now, and in the future +it may be possible to deprecate some of the resulting traits. + +# Alternatives + +It seems clear that we need *some* convention here. Other possible +suffixes would be `Util` or `Methods`, but `Ext` is both shorter and +connects to the name of the pattern. + +# Drawbacks + +In general, extension traits tend to require additional imports -- +especially painful when dealing with object safety. However, this is +more to do with the language as it stands today than with the +conventions in this RFC. + +In the long run, one way to mitigate these problems would be to add a +general "prelude" facility for external libraries that makes it +possible to globally import a small set of names from the crate. Some +early investigations of such a feature are already under way, but are +outside the scope of this RFC. From 97ecd04ed5080ca7d2d44d2051ebd79115e187c0 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 25 Nov 2014 15:52:06 -0800 Subject: [PATCH 2/2] Prelude clarifications --- text/0000-extension-trait-conventions.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/text/0000-extension-trait-conventions.md b/text/0000-extension-trait-conventions.md index 431392f7ccb..07079aae5b7 100644 --- a/text/0000-extension-trait-conventions.md +++ b/text/0000-extension-trait-conventions.md @@ -106,6 +106,12 @@ should follow a similar convention, putting together the type/trait name and the qualifications, together with the `Ext` suffix: `IteratorAddExt`. +### What about `Prelude`? + +A [previous convention](https://github.com/rust-lang/rfcs/pull/344) +used a `Prelude` suffix for extension traits that were also part of +the `std` prelude; this new convention deprecates that one. + ## Future proofing In the future, the need for many of these extension traits may @@ -132,8 +138,11 @@ especially painful when dealing with object safety. However, this is more to do with the language as it stands today than with the conventions in this RFC. -In the long run, one way to mitigate these problems would be to add a -general "prelude" facility for external libraries that makes it -possible to globally import a small set of names from the crate. Some -early investigations of such a feature are already under way, but are -outside the scope of this RFC. +Libraries are already starting to export their own `prelude` module +containing extension traits among other things, which by convention is +glob imported. + +In the long run, we should add a general "prelude" facility for +external libraries that makes it possible to *globally* import a small +set of names from the crate. Some early investigations of such a +feature are already under way, but are outside the scope of this RFC.