Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple overloads of a single operator. #2961

Closed
brendanzab opened this issue Jul 19, 2012 · 5 comments
Closed

Multiple overloads of a single operator. #2961

brendanzab opened this issue Jul 19, 2012 · 5 comments
Labels
A-typesystem Area: The type system E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot.

Comments

@brendanzab
Copy link
Member

Are multiple overloads of a single operator going to be supported in the future?

To illustrate what I mean here's a simple matrix implementation:

impl mat4_ops for mat4 {

    // ...

    fn *(f: float) -> mat4 { ... }

    fn *(v: vec4) -> vec4 { ... }

    fn *(m: mat4) -> mat4 { ... }

    // ...

}

Here's a simple example that you can compile right away:

import io::println;

type vec3 = { x: float, y: float, z: float };

impl vec3_ops for vec3 {

    // Scales the vector
    fn *(f: float) -> vec3 {
        { x: self.x * f, y: self.y * f,  z: self.z * f }
    }

    // dot product
    fn *(v: vec3) -> float {
        (self.x * v.x) + (self.y * v.y) + (self.z * v.z)
    }

}

fn main() {

    let v1: vec3 = { x: 2.3, y: 8.4, z: 7.9  };
    let v2: vec3 = { x: 4.0, y: 6.7, z: 10.5 };

    let v3 = v1 * 2f;
    println(#fmt("%.2f, %.2f, %.2f", v3.x, v3.y, v3.z));

    let dot_product = v1 * v2;
    println(#fmt("%.2f", dot_product));

}

And this is the error I get when I compile using rust version 0.3:

operatorTest.rs:27:31: 27:33 error: mismatched types: expected `float` but found `{x: float,y: float,z: float}` (float vs record)
operatorTest.rs:27         let dot_product = v1 * v2;
                                                  ^~
operatorTest.rs:28:29: 28:40 error: mismatched types: expected `float` but found `vec3` (float vs record)
operatorTest.rs:28         println(#fmt("%.2f", dot_product));
                                                ^~~~~~~~~~~
note: in expansion of #fmt
operatorTest.rs:28:16: 28:42 note: expansion site
error: aborting due to 2 previous errors
@catamorphism
Copy link
Contributor

In git HEAD, this example gets rejected for a different reason: now that we check for coherence, we no longer have impls that don't implement a trait. So you would have to change vec3_ops so it implements a trait.

You could rewrite the code using a trait, but then the coherence checker would reject it, because you would be doing something like:

trait times<IN, OUT> {
  fn *(&&x: IN) -> OUT;
}

impl of times<float, vec3> for vec3 {
  fn *(f: float) -> vec3 {
        { x: self.x * f, y: self.y * f,  z: self.z * f }
  }
}

impl of times<vec3, float> for vec3 {
  fn *(v: vec3) -> float {
        (self.x * v.x) + (self.y * v.y) + (self.z * v.z)
  }
}

The coherence checker would reject the two instances of vec3 as overlapping, even though they don't really overlap. (This is similar to Haskell, where in Haskell 98, analogous code would be rejected, but if you enabled GHC's FlexibleInstances and FlexibleContexts extensions (which everyone does in practice), it would be accepted.)

Right now it seems unlikely to me that we will support this form of overloading via traits. Among people on the core team, I think most of us think that overloading adds a lot of cognitive burden for people trying to read and understand new code.

It's a good question, and one that the past decade or so of work on type class extensions in Haskell addresses, but in Rust we're trying to keep our goals modest ;-)

@brendanzab
Copy link
Member Author

Ahh, I didn't realise that that there were no function overloads at all in rust. Whilst it is slightly annoying as I want to be able to keep my code terse, I can see the reasoning in terms of cognitive burden.

I may have missed it, but I didn't notice any mention of this in the tutorial. It might be worthwhile making this decision (and the reasoning behind it) a little more clear to people coming from other languages.

You know, I almost wish there were no operator overloads now to save people from false expectations. I'll probably shift most of my overloads to plain old functions to keep things consistent. Ie. my_matrix.mult_vec3(vector), my_matrix.mult_float(f), my_matrix.mult_mat3(other_matrix)

@catamorphism
Copy link
Contributor

@bjz - We allow overloads. We don't allow overloads of the same function with different return types. This is, I believe, similar to Java's typechecking rules.

@graydon
Copy link
Contributor

graydon commented Jul 23, 2012

Less than that even. We require overloads to differ by self (impl) type, and permit at most one use of a name per self type.

@pcwalton
Copy link
Contributor

As I pointed out on HN, I believe this works:

trait MatrixMultiplyRHS<Result> {
    fn mul(matrix: Matrix) -> Result;
}

impl<RHS:MatrixMultiplyRHS<Result>,Result> Matrix : Mul<RHS, Result> {
    fn mul(rhs: RHS) -> Result {
        rhs.mul(self)
    }
}

impl float : MatrixMultiplyRHS<Matrix> {
    fn mul(matrix: Matrix) -> Matrix {
        // ...implementation of matrix scalar multiply...
    }
}

impl Vector : MatrixMultiplyRHS<Vector> {
    fn mul(matrix: Matrix) -> Vector {
        // ... implementation of matrix multiply for vectors ...
    }
}

impl Matrix : MatrixMultiplyRHS<Matrix> {
    fn mul(matrix: Matrix) -> Matrix {
        // ... implementation of matrix multiply for matrices ...
    }
}

(Once Mul becomes a trait, of course.)

Feel free to reopen if this doesn't work for some reason.

flip1995 pushed a commit to flip1995/rust that referenced this issue Jun 30, 2022
…, r=dswij

`trivially_copy_pass_by_ref` fixes

fixes rust-lang#5953
fixes rust-lang#2961

The fix for rust-lang#5953 is overly aggressive, but the suggestion is so bad that it's worth the false negatives. Basically three things together:
* It's not obviously wrong
* It compiles
* It may actually work when tested

changelog: Don't lint `trivially_copy_pass_by_ref` when unsafe pointers are used.
changelog: Better track lifetimes when linting `trivially_copy_pass_by_ref`.
celinval added a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
Fixes needed due to renaming of a few items:
 - rust-lang#119063
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-typesystem Area: The type system E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot.
Projects
None yet
Development

No branches or pull requests

4 participants