From 8d5b133ab133cf4b9eef19f15efe77d9ecd4ce17 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 8 Mar 2015 02:58:14 -0500 Subject: [PATCH 1/3] overloaded assignment operations `a += b` --- text/0000-op-assign.md | 74 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 text/0000-op-assign.md diff --git a/text/0000-op-assign.md b/text/0000-op-assign.md new file mode 100644 index 00000000000..3ba083267f5 --- /dev/null +++ b/text/0000-op-assign.md @@ -0,0 +1,74 @@ +- Feature Name: op_assign +- Start Date: 2015-03-08 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Add the family of `[Op]Assign` traits to allow overloading assignment +operations like `a += b`. + +# Motivation + +We already let users overload the binary operations, letting them overload the +assignment version is the next logical step. Plus, this sugar is important to +make mathematical libraries more palatable. + +# Detailed design + +Add the following **unstable** traits to libcore and reexported them in stdlib: + +``` +// `+=` +#[lang = "add_assign"] +trait AddAssign { + fn add_assign(&mut self, &Rhs); +} + +// the remaining traits have the same signature +// (lang items have been omitted for brevity) +trait BitAndAssign { .. } // `&=` +trait BitOrAssign { .. } // `|=` +trait BitXorAssign { .. } // `^=` +trait DivAssign { .. } // `/=` +trait MulAssign { .. } // `*=` +trait RemAssign { .. } // `%=` +trait ShlAssign { .. } // `<<=` +trait ShrAssign { .. } // `>>=` +trait SubAssign { .. } // `-=` +``` + +Implement these traits for the primitive numeric types *without* overloading, +i.e. only `impl AddAssign for i32 { .. }`. + +Add an `op_assign` feature gate. When the feature gate is enabled, the compiler +will consider these traits when typecheking `a += b`. Without the feature gate +the compiler will enforce that `a` and `b` must be primitives of the same +type/category as it does today. + +Once we feel comfortable with the implementation we'll remove the feature gate +and mark the traits as stable. This can be done after 1.0 as this change is +backwards compatible. + +# Drawbacks + +None that I can think of. + +# Alternatives + +Alternatively, we could change the traits to take the RHS by value. This makes +them more "flexible" as the user can pick by value or by reference, but makes +the use slightly unergonomic in the by ref case as the borrow must be explicit +e.g. `x += &big_float;` vs `x += big_float;`. + +# Unresolved questions + +Are there any use cases of assignment operations where the RHS has to be taken +by value for the operation to be performant (e.g. to avoid internal cloning)? + +Should we overload `ShlAssign` and `ShrAssign`, e.g. +`impl ShlAssign for i32`, since we have already overloaded the `Shl` and +`Shr` traits? + +Should we overload all the traits for references, e.g. +`impl<'a> AddAssign<&'a i32> for i32` to allow `x += &0;`? From b78d276d65944ccb44c0a65e7e674af694430923 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 8 Mar 2015 12:20:17 -0500 Subject: [PATCH 2/3] add section on taking the RHS by ref vs by value --- text/0000-op-assign.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/text/0000-op-assign.md b/text/0000-op-assign.md index 3ba083267f5..8fb962ceaf5 100644 --- a/text/0000-op-assign.md +++ b/text/0000-op-assign.md @@ -50,6 +50,20 @@ Once we feel comfortable with the implementation we'll remove the feature gate and mark the traits as stable. This can be done after 1.0 as this change is backwards compatible. +## RHS: By ref vs by value + +This RFC proposes that the assignment operations take the RHS always by ref; +instead of by value like the "normal" binary operations (e.g. `Add`) do. The +rationale is that, as far as the author has seen in practice [1], one never +wants to mutate the RHS or consume it, or in other words an immutable view into +the RHS is enough to perform the operation. Therefore, this RFC follows in the +footsteps of the `Index` traits, where the same situation arises with the +indexing value, and by ref was chosen over by value. + +[1] It could be possible that the author is not aware of use cases where taking +RHS by value is necessary. Feedback on this matter would be appreciated. (See +the first unresolved question) + # Drawbacks None that I can think of. From 7bca6c58ae234976d9f94c58cf69e32a764fa33c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 20 Mar 2015 23:15:37 -0500 Subject: [PATCH 3/3] take RHS by value --- text/0000-op-assign.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/text/0000-op-assign.md b/text/0000-op-assign.md index 8fb962ceaf5..6657f0a982f 100644 --- a/text/0000-op-assign.md +++ b/text/0000-op-assign.md @@ -16,13 +16,13 @@ make mathematical libraries more palatable. # Detailed design -Add the following **unstable** traits to libcore and reexported them in stdlib: +Add the following **unstable** traits to libcore and reexported them in libstd: ``` // `+=` #[lang = "add_assign"] trait AddAssign { - fn add_assign(&mut self, &Rhs); + fn add_assign(&mut self, Rhs); } // the remaining traits have the same signature @@ -50,19 +50,22 @@ Once we feel comfortable with the implementation we'll remove the feature gate and mark the traits as stable. This can be done after 1.0 as this change is backwards compatible. -## RHS: By ref vs by value +## RHS: By value vs by ref -This RFC proposes that the assignment operations take the RHS always by ref; -instead of by value like the "normal" binary operations (e.g. `Add`) do. The -rationale is that, as far as the author has seen in practice [1], one never -wants to mutate the RHS or consume it, or in other words an immutable view into -the RHS is enough to perform the operation. Therefore, this RFC follows in the -footsteps of the `Index` traits, where the same situation arises with the -indexing value, and by ref was chosen over by value. +Taking the RHS by value is more flexible. The implementations allowed with +a by value RHS are a superset of the implementations allowed with a by ref RHS. +An example where taking the RHS by value is necessary would be operator sugar +for extending a collection with an iterator [1]: `vec ++= iter` where +`vec: Vec` and `iter impls Iterator`. This can't be implemented with the +by ref version as the iterator couldn't be advanced in that case. -[1] It could be possible that the author is not aware of use cases where taking -RHS by value is necessary. Feedback on this matter would be appreciated. (See -the first unresolved question) +[1] Where `++` is the "combine" operator that has been proposed [elsewhere]. +Note that this RFC doesn't propose adding that particular operator or adding +similar overloaded operations (`vec += iter`) to stdlib's collections, but it +leaves the door open to the possibility of adding them in the future (if +desired). + +[elsewhere]: https://github.com/rust-lang/rfcs/pull/203 # Drawbacks @@ -70,16 +73,14 @@ None that I can think of. # Alternatives -Alternatively, we could change the traits to take the RHS by value. This makes -them more "flexible" as the user can pick by value or by reference, but makes -the use slightly unergonomic in the by ref case as the borrow must be explicit -e.g. `x += &big_float;` vs `x += big_float;`. +Take the RHS by ref. This is less flexible than taking the RHS by value but, in +some instances, it can save writing `&rhs` when the RHS is owned and the +implementation demands a reference. However, this last point will be moot if we +implement auto-referencing for binary operators, as `lhs += rhs` would actually +call `add_assign(&mut lhs, &rhs)` if `Lhs impls AddAssign<&Rhs>`. # Unresolved questions -Are there any use cases of assignment operations where the RHS has to be taken -by value for the operation to be performant (e.g. to avoid internal cloning)? - Should we overload `ShlAssign` and `ShrAssign`, e.g. `impl ShlAssign for i32`, since we have already overloaded the `Shl` and `Shr` traits?