|
| 1 | +- Start Date: 2015-02-19 |
| 2 | +- RFC PR: (leave this empty) |
| 3 | +- Rust Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Allow inherent implementations on types outside of the module they are defined in, |
| 8 | +effectively reverting [RFC PR 155](https://github.com/rust-lang/rfcs/pull/155). |
| 9 | + |
| 10 | +# Motivation |
| 11 | + |
| 12 | +The main motivation for disallowing such `impl` bodies was the implementation |
| 13 | +detail of fake modules being created to allow resolving `Type::method`, which |
| 14 | +only worked correctly for `impl Type {...}` if a `struct Type` or `enum Type` |
| 15 | +were defined in the same module. The old mechanism was obsoleted by UFCS, |
| 16 | +which desugars `Type::method` to `<Type>::method` and perfoms a type-based |
| 17 | +method lookup instead, with path resolution having no knowledge of inherent |
| 18 | +`impl`s - and all of that was implemented by [rust-lang/rust#22172](https://github.com/rust-lang/rust/pull/22172). |
| 19 | + |
| 20 | +Aside from invalidating the previous RFC's motivation, there is something to be |
| 21 | +said about dealing with restricted inherent `impl`s: it leads to non-DRY single |
| 22 | +use extension traits, the worst offender being `AstBuilder` in libsyntax, with |
| 23 | +almost 300 lines of redundant method definitions. |
| 24 | + |
| 25 | +# Detailed design |
| 26 | + |
| 27 | +Remove the existing limitation, and only require that the `Self` type of the |
| 28 | +`impl` is defined in the same crate. This allows moving methods to other modules: |
| 29 | +```rust |
| 30 | +struct Player; |
| 31 | + |
| 32 | +mod achievements { |
| 33 | + struct Achievement; |
| 34 | + impl Player { |
| 35 | + fn achieve(&mut self, _: Achievement) {} |
| 36 | + } |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +# Drawbacks |
| 41 | + |
| 42 | +Consistency and ease of finding method definitions by looking at the module the |
| 43 | +type is defined in, has been mentioned as an advantage of this limitation. |
| 44 | +However, trait `impl`s already have that problem and single use extension traits |
| 45 | +could arguably be worse. |
| 46 | + |
| 47 | +# Alternatives |
| 48 | + |
| 49 | +- Leave it as it is. Seems unsatisfactory given that we're no longer limited |
| 50 | + by implementation details. |
| 51 | + |
| 52 | +- We could go further and allow adding inherent methods to any type that could |
| 53 | + implement a trait outside the crate: |
| 54 | + ```rust |
| 55 | + struct Point<T> { x: T, y: T } |
| 56 | + impl<T: Float> (Vec<Point<T>>, T) { |
| 57 | + fn foo(&mut self) -> T { ... } |
| 58 | + } |
| 59 | + ``` |
| 60 | + |
| 61 | + The implementation would reuse the same coherence rules as for trait `impl`s, |
| 62 | + and, for looking up methods, the "type definition to impl" map would be replaced |
| 63 | + with a map from method name to a set of `impl`s containing that method. |
| 64 | + |
| 65 | + *Technically*, I am not aware of any formulation that limits inherent methods |
| 66 | + to user-defined types in the same crate, and this extra support could turn out |
| 67 | + to have a straight-foward implementation with no complications, but I'm trying |
| 68 | + to present the whole situation to avoid issues in the future - even though I'm |
| 69 | + not aware of backwards compatibility ones or any related to compiler internals. |
| 70 | + |
| 71 | +# Unresolved questions |
| 72 | + |
| 73 | +None. |
0 commit comments