More than one operator overload in Rust
Asked Answered
T

2

7

I know it is possible by using traits to implement operator overloading in Rust. In C++ is also possible to have multiple operator overloading for the same operator and the same struct, and switch on/off which operator to use.

Is it possible to do the same as the following C++ code in Rust? Possibly in the same source file?

struct S
{
    int value;
};

namespace first
{
S operator+(S lhs, S rhs)
{
    return S{ lhs.value + rhs.value };
}
}

namespace second
{
S operator+(S lhs, S rhs)
{
    return S{ lhs.value * rhs.value };
}
}

int main()
{
    S s1{5};
    S s2{10};
    {
        using namespace first;
        S s3 = s1 + s2;
        std::cout << s3.value << std::endl;
    }
    {
        using namespace second;
        S s3 = s1 + s2;
        std::cout << s3.value << std::endl;
    }
}
Teressaterete answered 4/9, 2020 at 12:32 Comment(4)
I have one question only - why would you like to create such error prone and convoluted code in any programming language?Embree
But if you are going to create such error prone and convoluted code, C++ gives you plenty of rope to shoot yourself in the foot. I'm not so sure Rust provides that kind of ammunition. C++ is your huckleberry.Ebba
I didn't know you could do this in C++ and now I wish I could forget.Signalment
@trentcl: Then again, this code samples violates 2 coding rules => never use using namespace, and always place operator overloads in the namespace of one of their arguments so they can be found by ADL.Binominal
B
7

The idiomatic answer in Rust to:

How do I use different overloads of +, <, ... for my Type?

is to wrap the type up and implement the operator on the wrapper.

Example on the playground

#[derive(Clone, Copy, Debug)]
struct S(i32);

#[derive(Debug)]
struct Adder(pub S);

impl std::ops::Add<S> for Adder {
    type Output = S;

    fn add(self, other: S) -> S { S(self.0.0 + other.0) }
}

impl std::ops::Add<Adder> for S {
    type Output = S;

    fn add(self, other: Adder) -> S { S(self.0 + other.0.0) }
}

#[derive(Debug)]
struct Multiplier(pub S);

impl std::ops::Add<S> for Multiplier {
    type Output = S;

    fn add(self, other: S) -> S { S(self.0.0 * other.0) }
}

impl std::ops::Add<Multiplier> for S {
    type Output = S;

    fn add(self, other: Multiplier) -> S { S(self.0 * other.0.0) }
}

fn main() {
    let one = S(5);
    let two = S(10);

    println!("{:?}", one + Adder(two));
    println!("{:?}", Adder(one) + two);
    
    println!("{:?}", one + Multiplier(two));
    println!("{:?}", Multiplier(one) + two);
}

This idiom can be seen in the standard library where the std::num::Wrapping type can be used to wrap an integer in which case addition, subtraction, and multiplication are re-defined to use modulo arithmetic.

Binominal answered 4/9, 2020 at 15:43 Comment(0)
F
4

Is it possible to do the same as the following C++ code in Rust? Possibly in the same source file?

No, it is not (not exactly) for the following reasons:

  1. A struct cannot implement the same trait twice (why would you want that?)
  2. There is no function overloading in rust

Regarding what you want in your code, you can approximate it like this:


#[derive(Debug)]
struct S(i32);

mod first {
    use crate::S;
    pub trait Foo {
        fn bar(self, s: S) -> S;
    }
    
    impl Foo for S  {
        fn bar(self, s: S) -> S {
            S(self.0 + s.0)
        }
    }
}

mod second {
    use crate::S;
    pub trait Foo {
        fn bar(self, s: S) -> S;
    }
    
    impl Foo for S  {
        fn bar(self, s: S) -> S {
            S(self.0 * s.0)
        }
    }
}

fn main() {
    let first_res = first::Foo::bar(S(1), S(2));
    let second_res = second::Foo::bar(S(1), S(2));

    println!("{:?}, {:?}", first_res, second_res);
    
    {
        use first::Foo;
        println!("{:?}", S(1).bar(S(2)));
    }
    
    {
        use second::Foo;
        println!("{:?}", S(1).bar(S(2)));
    }
}

Playground

Notice that the Foo traits are indeed different traits for the compiler. So you are implementing two different traits, not one. For the same reason, both bar methods are different ones too.

Fireresistant answered 4/9, 2020 at 12:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.