# Rust's operator overloading doesn't scale

**URL:** <https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408>\
**Category:** ideas (deprecated)\
**Created:** [August 18, 2014, 11:30pm UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408 "2014-08-18T23:30:01Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![SiegeLord](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/siegelord/32/191_2.png) [@SiegeLord](https://internals.rust-lang.org/u/SiegeLord)\
**Post date:** [August 18, 2014, 11:30pm UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408/1 "2014-08-18T23:30:02Z")

</div>

So I’ve been toying a bit with make a math library based on lazy evaluation/expression templates. That is, when you add two matrices, for example, instead of allocating a new matrix with the result, it returns an object that references the two operands and allows you to look up the results lazily, element by element. For example, the type of `b` in this code

```rust
let a = &mat![1u, 2u;
              3u, 4u];
let b = a + -(a * a);

```

is

```rust
algebloat::bin_ops::MatrixBinOp<&algebloat::matrix::Matrix,algebloat::un_ops::MatrixUnOp<algebloat::bin_ops::MatrixBinOp<&algebloat::matrix::Matrix,&algebloat::matrix::Matrix,algebloat::bin_ops::OpMul>,algebloat::un_ops::OpNeg>,algebloat::bin_ops::OpAdd>

```

or (cleaned up):

```rust
MatrixBinOp<&Matrix, MatrixUnOp<MatrixBinOp<&Matrix, &Matrix, OpMul>, OpNeg>, OpAdd>

```

If this reminds of the types you get from chaining `Iterator`s, that’s because `Iterator` is based on the same concept. This ends up being optimized pretty well by the compiler and leads to efficient code (if somewhat distasteful error messages).

Now, let’s say I wanted to have a generic function where I take two of these lazy objects and perform some mathematical operation on them (I’ll limit myself to just addition and multiplication). Here’s what the function signature ends up looking like:

```rust
fn add_or_mul<
    T: MatrixRawGet + SameShape + MatrixShape + Clone + 
        Add<T, MatrixBinOp<T, T, OpAdd>> +
        Mul<T, MatrixBinOp<T, T, OpMul>> +
        
        Add<MatrixBinOp<T, T, OpAdd>, MatrixBinOp<T, MatrixBinOp<T, T, OpAdd>, OpAdd>> +
        Add<MatrixBinOp<T, T, OpMul>, MatrixBinOp<T, MatrixBinOp<T, T, OpMul>, OpAdd>> +
        Mul<MatrixBinOp<T, T, OpMul>, MatrixBinOp<T, MatrixBinOp<T, T, OpMul>, OpMul>> +
        Mul<MatrixBinOp<T, T, OpAdd>, MatrixBinOp<T, MatrixBinOp<T, T, OpAdd>, OpMul>> +
        
        Add<U, MatrixBinOp<T, U, OpAdd>> +
        Mul<U, MatrixBinOp<T, U, OpMul>> +
        
        Add<MatrixBinOp<T, U, OpAdd>, MatrixBinOp<T, MatrixBinOp<T, U, OpAdd>, OpAdd>> +
        Add<MatrixBinOp<T, U, OpMul>, MatrixBinOp<T, MatrixBinOp<T, U, OpMul>, OpAdd>> +
        Mul<MatrixBinOp<T, U, OpMul>, MatrixBinOp<T, MatrixBinOp<T, U, OpMul>, OpMul>> +
        Mul<MatrixBinOp<T, U, OpAdd>, MatrixBinOp<T, MatrixBinOp<T, U, OpAdd>, OpMul>>
        ,
    U: MatrixRawGet + SameShape + MatrixShape + Clone +
        Add<U, MatrixBinOp<U, U, OpAdd>> + 
        Mul<U, MatrixBinOp<U, U, OpMul>> +
        
        Add<MatrixBinOp<U, U, OpAdd>, MatrixBinOp<U, MatrixBinOp<U, U, OpAdd>, OpAdd>> +
        Add<MatrixBinOp<U, U, OpMul>, MatrixBinOp<U, MatrixBinOp<U, U, OpMul>, OpAdd>> +
        Mul<MatrixBinOp<U, U, OpMul>, MatrixBinOp<U, MatrixBinOp<U, U, OpMul>, OpMul>> +
        Mul<MatrixBinOp<U, U, OpAdd>, MatrixBinOp<U, MatrixBinOp<U, U, OpAdd>, OpMul>> +
        
        Add<T, MatrixBinOp<U, T, OpAdd>> +
        Mul<T, MatrixBinOp<U, T, OpMul>> +
        
        Add<MatrixBinOp<U, T, OpAdd>, MatrixBinOp<U, MatrixBinOp<U, T, OpAdd>, OpAdd>> +
        Add<MatrixBinOp<U, T, OpMul>, MatrixBinOp<U, MatrixBinOp<U, T, OpMul>, OpAdd>> +
        Mul<MatrixBinOp<U, T, OpMul>, MatrixBinOp<U, MatrixBinOp<U, T, OpMul>, OpMul>> +
        Mul<MatrixBinOp<U, T, OpAdd>, MatrixBinOp<U, MatrixBinOp<U, T, OpAdd>, OpMul>>
    >(a: T, b: U)
{
}

```

Quite a mouthful, no? In fact, I estimate that it takes `n * (n + 1) * m * m` operator-specific type bounds (where `n` is the number of operations and `m` is the number of parameters). I overload 5 operators in my library, so for 2 parameters I’d need **120** of those bounds! That’s most likely longer than the function itself if I lay them out like this. It’s easy to get over a thousand lines in this signature with relatively small number of function arguments.

The kicker is that it doesn’t even work, because all those `add` and `mul` methods conflict, and there’s no easy way to disambiguate them (is this a bug?).

This explosion isn’t really just because of of this laziness. Consider matrix multipication, something introduced using a trait in my library. If I wanted to do only that, then my function signature is a lot simpler:

```rust
fn mat_mul<
    T: MatrixRawGet + MatrixShape + Clone,
    U: MatrixRawGet + MatrixShape + Clone
    >(a: T, b: U)
{
}

```

That works just fine, there are no second order growth terms. The reason why this works here is because in my library I have this implementation:

```rust
impl<LHS: MatrixShape + MatrixRawGet,
     RHS: MatrixShape + MatrixRawGet>
MatrixMultiply<RHS> for LHS
{
}

```

I cannot do that for the operator overloading traits, due to coherence rules.

So my question is… how to resolve this? From my perspective, operator overloading traits are fundamentally flawed as they simply don’t work with coherence. I must have that kind of generic implementation for the generic usage of my types to be sane.

My idea to resolve this is to abandon traits as a method for operator overloading, and do things like is done by the old-Rust, Haskell, Scala, C++ and probably _every_ other language out there. That is, operators would literally just call some magically named functions. Thus, what I’d be able to do is this:

```rust
trait MyAdd<T, R>
{
    #[operator(+)]
    fn add(&self, rhs: &T) -> R;
}

```

And the compiler, when seeing `a + b` would desugar it to `a.+(b)`. You’d lose the auto-ref behavior of the right-hand side, but frankly, if you are bothered by it, you should picket for the addition of auto-ref in general argument positions… what makes operators so special, after all? Either way, for built-in types, the `rhs` could be taken by value. I.e. the `Add` definition in `core::ops` would become:

```rust
trait Add<T, R>
{
    #[operator(+)]
    fn add(&self, rhs: T) -> R;
}

```

This would preserve the current syntax for `Copy` types. In particular, I am not advocating for the removal of the traits in `core::ops`, but rather demoting their special status as the sole way to obtain operator overloading. If I cared for generic code outside my library wanting to interface with it via the `core::ops` traits, I can also implement them.

Some other alternatives I heard mentioned was to add a way to declare custom operators… but let’s be serious here. I want this addressed before 1.0 fixes these broken traits in stone. Even if that were an option, why leave the common operators crippled?

I’m going to think about this some more and write-up an RFC, but first I wanted to check if there were any other ideas to fixing my issues.

---

<div class="post-metadata">

**Author:** ![bjadamson](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjadamson/32/52_2.png) [@bjadamson](https://internals.rust-lang.org/u/bjadamson)\
**Post date:** [August 19, 2014, 9:33am UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408/2 "2014-08-19T09:33:10Z")

</div>

> [@SiegeLord](#):
>
> ow to resolve this? From my perspective, operator overloading traits are fundamentally flawed as they simply don't wor

I think a majority of these issues are address by associated items.

> <https://github.com/aturon/rfcs/blob/associated-items/active/0000-associated-items.md>

> <https://github.com/rust-lang/rust/pull/16377>
>
> This is waiting on an RFC, but this basic functionality should be
> straightforwar…d. The implementation essentially desugars during type
> collection and AST type conversion time into the parameter scheme we
> have now.
> 
> r? @nikomatsakis 
> 
> (addendum by \`pnkfelix\`: the feature gate's name is \`associated\_types\`)

---

<div class="post-metadata">

**Author:** ![SiegeLord](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/siegelord/32/191_2.png) [@SiegeLord](https://internals.rust-lang.org/u/SiegeLord)\
**Post date:** [August 19, 2014, 11:06am UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408/3 "2014-08-19T11:06:56Z")

</div>

I really don’t see how associated items help here at all. What is required are generic impls, so I don’t have to write _any_ operator overload type bounds in the signature. Associated items, still force me to specify all possible LHS types, and there are still quite a few of them.

---

<div class="post-metadata">

**Author:** ![erickt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/erickt/32/4498_2.png) [@erickt](https://internals.rust-lang.org/u/erickt)\
**Post date:** [August 20, 2014, 10:19pm UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408/4 "2014-08-20T22:19:35Z")

</div>

@SiegeLord: I think @aturon's abstract return type RFC would help with this situation. It's currently closed, but I believe the plan is to revisit this at some point in the future.

> <https://github.com/rust-lang/rfcs/pull/105>

---

<div class="post-metadata">

**Author:** ![SiegeLord](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/siegelord/32/191_2.png) [@SiegeLord](https://internals.rust-lang.org/u/SiegeLord)\
**Post date:** [August 20, 2014, 10:38pm UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408/5 "2014-08-20T22:38:34Z")

</div>

I don’t think that RFC solves the same problem. My understanding of that RFC was that it allowed you to avoid writing out the complete type, and instead describe it with the traits it implements. In this case, this would not be of any help since it’s that description that is the problem here.

I agree that it would be useful for the return types as they are correspondingly ugly (just like with `Iterator`).

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [March 25, 2019, 8:22am UTC](https://internals.rust-lang.org/t/rusts-operator-overloading-doesnt-scale/408/6 "2019-03-25T08:22:48Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
