Is it only me consider std::ops traits are badly designed?

std::ops

The trait name of overloadable operators are Add for +, BitAnd for &, etc. Their names are certainly meaningful for their original semantic meaning, that is the + operator is actually falling into one of the categories from dictionary:

  1. to unite or join so as to increase the number, quantity, size, or importance:
    to add two cups of sugar; to add a postscript to her letter; to add insult to injury.
  2. to find the sum of (often followed by up):
    Add this column of figures. Add up the grocery bills.
  3. to say or write further.
  4. to include (usually followed by in):
    Don't forget to add in the tip.

But when I refer + operator as string concatenation or / operator as path concatenation, then we should rename the Add trait to StrConcat and Div trait to PathConcat or whatever, you name it.

The problem here I think it is the original designer mix some of the semantic into the trait names and function names which are responsible for a much boarder case than its original meaning. And the C++'s operator+, etc. naming is much more approachable. At least I don't need to look up the doc when I want to overload an operator. (e.g., Is it Add or Plus?)

"Badly designed" and "badly named" are completely different things. I thought this was going to be about how + * / - take self by value making it hard to make ergonomic and performant operations on non-Copy types.

(Honestly, I forget you can use + to concatenate strings in Rust. push_str is usually more expressive of what I want to do.)

4 Likes

Since it only contains a list of traits and theirs impls. There is little design space in std::ops.

Rust's original designers took the position that you shouldn't be overloading these operators with other semantics. Several actually consider String + &str a mistake that shouldn't have made it into 1.0. (But it did, so it's here to stay whichever side of the argument you're on.)

C++ and Haskell are the prime examples of overloaded operator semantics introducing confusion. In C++ you can't know whether << means "left shift" or "stream pipe" or something else entirely without knowing all the types involved, all the possible coersions, and all the implementations of operator<< for the involved types and the types it converts to. Though Haskell's slightly better (through strong community shaming for misusing "common" operators), it has the same problem of not knowing what the semantics of operators are without tracing all of the type inference (which is near impossible for humans in Haskell).

With Rust, because you have trait Add and not trait Plus, + has the semantics of adding two things together. This is desirable, because whenever you see + it's an addition operation. Some people suggest that you should even respect a + b == b + a when implementing Add (which is why String + &str is problematic).

TL;DR: Rust's opinion is that you shouldn't overload an operator to do anything other than it's drilled-into-every-grade-schooler mathematical definition.

3 Likes

This makes sense. Also, it seems that I seldom overload operators.