From 1841fa3c7e364aba8cec05fc7fbe13955c38d8bd Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Wed, 15 Oct 2014 14:24:46 -0400 Subject: [PATCH 1/3] Ad-hoc operators --- active/0000-ad-hoc-operators.md | 164 ++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 active/0000-ad-hoc-operators.md diff --git a/active/0000-ad-hoc-operators.md b/active/0000-ad-hoc-operators.md new file mode 100644 index 00000000000..5b501d9299f --- /dev/null +++ b/active/0000-ad-hoc-operators.md @@ -0,0 +1,164 @@ +- Start Date: 2014-10-15 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Add ad-hoc operator overloading to Rust and remove the current-trait based +overloading. 'ad-hoc' in this RFC means that when the compiler looks for an +implementation of an operator overload it does so by looking for a specific +method, rather than for a specific trait. The current trait-based system is a +strict subset of this enhancement, which means this RFC is backwards +compatible. + +# Motivation + +## Rust specific motivation + +The current system for operator overloading involves implementing a specific +trait (decorated with a lang-item attribute) that the compiler will then look +for when the operator syntax is encountered. This is problematic, because +implementing a trait carries with it a few restrictions: + +* The relevant method's signature cannot be changed + * The method cannot be declared `unsafe` + * No lifetimes can be added/removed to the methods that are not present in + the original trait + * The methods may not be made private + * The methods may not have additional generics + +* The relevant trait implementation has to fulfill coherence requirements since + the operator overloading traits are 3rd party to most of the Rust's library + ecosystem + +The first issue in particular is often felt, for example when attempting to +define an `unsafe` variant of indexing and slicing. + +## Other languages + +Major languages that are similar to Rust use ad-hoc operator overloading (e.g. +Haskell, Scala, C++, D). The author is not aware of a language with trait-like +constructs that use them as the sole method of operator overloading. Haskell in +particular has a type class that is typically instantiated to provide operator +overloading, but that type class merely has specially named functions (as +Haskell allows symbolic function names). + +## Philosophical points + +In many ways, ad-hoc operator overloading is identical to having multiple +traits having a name with the same method. While this is discouraged, it is +still allowed to happen. There is no additional lack of clarity of this code +calling some arbitrary method: + +```rust +a + b +``` + +versus this code using a trait (or maybe inherent, who knows!) method name: + +```rust +a.add(&b) +``` + +In the latter case only by examining the traits available at call time as well +as the inherent methods can the reader be sure about what code is actually +evoked. This is not seen as a problem for normal methods, and therefore +shouldn't be a problem for operators. + +# Detailed design + +## General usage + +A new attribute `#[operator="xxx"]` would be introduced that can be applied to +inherent and trait methods, like so: + +```rust +trait MyTrait { + #[operator="add"] + fn add(&self, rhs: &uint) -> uint; +} + +struct S; + +impl S { + #[operator="index"] + unsafe fn index(&self, index: &uint) -> uint; +} + +impl MyTrait for S { + // operator attribute invalid here + fn add(&self, rhs: &uint) -> uint; +} +``` + +`"xxx"` in the above definition would be replaced by a string corresponding to +each operator (the author suggests using the current lang-item names). Invalid +method signatures are accepted, but are naturally detected when the de-sugaring +happens at the call site. + +## Possible implementation + +When the compiler sees a decorated method, it adds a duplicate, un-typeable, +entry to the set of methods this type implements (or this trait provides). E.g. +for `#[operator = "add"]` it will create a method named `` (normally an +invalid method name). This new entry acts as an alias to the regular method, +which is still available under its raw name for disambiguation purposes. When +the compiler sees an operator being used, it will rewrite the expression to +some de-sugaring. E.g. this code: + +```rust +a + b +``` + +would be transformed to this (by constructing the AST directly, rather than +going through the lexer): + +``` +a.(&b) +``` + +The exact de-sugaring should be chosen such that the methods of the current +operator overloading traits continue to work (i.e. the desugaring will +typically auto-borrow the RHS). + +## Fate of the current operator overloading traits + +The current traits will remain in place, but cease to be decorated by the +lang-items (and thus those lang-items will be removed). E.g. this current trait: + +```rust +#[lang="add"] +pub trait Add { + fn add(&self, rhs: &RHS) -> Result; +} +``` + +would be changed to: + +```rust +pub trait Add { + #[operator="add"] + fn add(&self, rhs: &RHS) -> Result; +} +``` + +The user code would not be affected. + +# Drawbacks + +There are no drawbacks. + +# Alternatives + +The alternative to this RFC is to add tons of traits for each possible +variation in method signature and/or forbid some arguably valid uses of +operator overloading. + +# Unresolved questions + +Although this RFC makes operator overloading a lot more flexible, it is still +constrained by the de-sugaring chosen for a particular operator. E.g. you won't +be able to create an `+` operator overload that moves the RHS. Even though you +can mark such a method with `#[operator="add"]`, the de-sugaring will borrow +the RHS and cause a type-check error. Addressing this issue is orthogonal to +this RFC. From 63a503c21cfb860a778bdf74aec0321047748957 Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Wed, 15 Oct 2014 14:26:37 -0400 Subject: [PATCH 2/3] Reword --- active/0000-ad-hoc-operators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active/0000-ad-hoc-operators.md b/active/0000-ad-hoc-operators.md index 5b501d9299f..dd7bfff7cfc 100644 --- a/active/0000-ad-hoc-operators.md +++ b/active/0000-ad-hoc-operators.md @@ -25,7 +25,7 @@ implementing a trait carries with it a few restrictions: * No lifetimes can be added/removed to the methods that are not present in the original trait * The methods may not be made private - * The methods may not have additional generics + * The methods may not have additional type parameters * The relevant trait implementation has to fulfill coherence requirements since the operator overloading traits are 3rd party to most of the Rust's library From cf7163a8fc066f6b36204af05da33da22a3056c8 Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Wed, 15 Oct 2014 15:19:03 -0400 Subject: [PATCH 3/3] Mention operator overloading traits with multiple methods --- active/0000-ad-hoc-operators.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/active/0000-ad-hoc-operators.md b/active/0000-ad-hoc-operators.md index dd7bfff7cfc..4a53d8222a8 100644 --- a/active/0000-ad-hoc-operators.md +++ b/active/0000-ad-hoc-operators.md @@ -92,9 +92,9 @@ impl MyTrait for S { ``` `"xxx"` in the above definition would be replaced by a string corresponding to -each operator (the author suggests using the current lang-item names). Invalid -method signatures are accepted, but are naturally detected when the de-sugaring -happens at the call site. +each operator (the author suggests using the method names of the current +operator overloading traits). Invalid method signatures are accepted, but are +naturally detected when the de-sugaring happens at the call site. ## Possible implementation @@ -142,6 +142,9 @@ pub trait Add { } ``` +For operator overloading traits with multiple methods, each method gets its own +version of the `#[operator]` attribute. + The user code would not be affected. # Drawbacks