Using a mathematical operator for a mutating operation is a terribly bad idea.
While this technically mutates behind the scenes, it moves the vector by value so, from the user’s perspective, vec![1,2,3] + 4 creates an entirely new vector vec![1,2,3,4].
vec![1,2,3] + 4
vec![1,2,3,4]
Python doesn’t do it, Haskell has a separate operator for it. Might be a bad idea for type inference reasons.
Good point. Personally I’d prefer to allow vec![1u,2,3] + [1u] (impl<T> Add<[T]> for Vec<T>); as far as I know, this shouldn’t require any allocation.
vec![1u,2,3] + [1u]
impl<T> Add<[T]> for Vec<T>
FYI, you can use less horrible-looking a + &[x] instead (playpen)
a + &[x]
This shouldn’t affect type inference at all - it’s just another trait implementation. The situation where the RHS can be either a container or an element of the same container is impossible - the former includes the latter: Vec<T> and T, String and char, etc.
Vec<T>
T
String
char
Add<[T]> can’t be defined because add() method in Add<T> takes T by value, which means that it can’t have T: ?Sized in principle. Also currently vec![1, 2, 3] + [1u].as_slice() doesn’t require an allocation too.
Add<[T]>
add()
Add<T>
T: ?Sized
vec![1, 2, 3] + [1u].as_slice()
Got it. I thought there was some way to write fn add<A: [uint]>(items: A) but I guess there isn’t.
fn add<A: [uint]>(items: A)
Trivial example where this affects type inference: vec![] + vec![].
vec![] + vec![]
Please god no. Operator overloading is butts. Just call push and help us figure out a better general syntax for chaining method calls (with! { foo: pop(); push(1); push(2); } or some crap like that).
push
with! { foo: pop(); push(1); push(2); }
This also seems bad because it’d be overloading the addition operator for concatenation. I personally don’t like this for strings, either.
I’m with Gankro. This kind of abuse of operator overloading is one of the things that makes C++ so horrible. Vec.push() has nothing to do with the mathematical concept of addition, and it has none of its properties. Operator overloading makes code harder to read / understand (for humans), and it makes compilers more complex and less efficient, since they need to figure out which op+ you mean.
So, I vote against op+ for Vec.
It’s not mutating, it consumes the original Vec.
There is an RFC somewhere for using the ++ operator for concatenation, following Haskell’s example. I prefer that approach because it doesn’t have any relation to the mathematical addition operator.
While appending to a list is different in some ways from addition, they are both operations on a monoid and so share e.g. the associative property. You can even define the natural numbers in terms of lists of 1s; then addition is concatenation.
That’s not to say that + is necessarily a good list concatenation operator; aside from the mathematical similarities we do need to take into account what the operator actually makes humans think of.
vec![1, 2, 3] + 4 should produce vec![5, 6, 7].
vec![1, 2, 3] + 4
vec![5, 6, 7]
P.S. The syntax I’d like to see is
vec![1, 2, 3].into_iter().fold(Vec::new(), |mut a, x| a `push` x)
This is what I proposed on a recent (pre-1.0) PR that was to remove the + op for Vecs and Strings. Having a non-commutative addition for vec types is a footgun.
Vec
for ++ or -- or ~~ or whatever operator we may come up with. Anything but +.
++
--
~~
+
Somewhat OT but, is there any reason add wasn’t defined as follows where Other is defined to be either the LHS or the RHS (i.e. sidedness is undefined)?
Other
trait Add<Other=Self> where Other: Add<Self, Output=Self::Output> { type Output; // ... }
In math the operand order for addition is not important but the same goes for Mul.
Mul
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.