#operator #operator-overloading #concise #sugar #intuitive #i32

no-std operator-sugar

Makes operator overloading in Rust more concise and intuitive

3 releases

0.1.2 Aug 12, 2019
0.1.1 Aug 12, 2019
0.1.0 Aug 10, 2019

#383 in No standard library

Apache-2.0

16KB
199 lines

operator-sugar

Makes operator overloading in Rust more concise and intuitive.

See the documentation for details.


lib.rs:

This crate provides simple macros that serve as syntactic sugar to make overloading operators in Rust easier.

The basic syntax for binary operators is in this format:

struct Left(i32);
struct Right(i32);
struct Answer(i32);

operator!(Left, Right: a + b -> Answer {
    // Implementation of the operator function here
    Answer(a.0 + b.0)
});

For unary operators:

struct Operand(i32);
struct Answer(i32);

operator!(Operand: -a -> Answer {
    Answer(-a.0)
});

Meta Attributes

Attributes can be applied to the impl block (which implements e.g. Add) and the fn block respectively:

struct Left(i32);
struct Right(i32);
struct Answer(i32);

operator!(
    #[doc("This attribute will be applied on the `impl` block")] Left, Right:
    #[doc("This attribute will be applied on the `fn` block")] a + b -> Answer {
        Answer(a.0 + b.0)
    });

Generics

Generics can be used on the three types and on the impl block.

Due to disambiguation, generic parameters for the impl block must be written in {} rather than <>.

use core::ops::Add;
struct Left<T>(T) where T: Add<i32, Output = i32>;
struct Right(i32);
struct Answer(i32);

operator!(
    {T: Add<i32, Output = i32>}
    Left<T>, Right: a + b -> Answer {
        Answer(a.0 + b.0)
    });

List of operators

For conciseness, these definitions are defined for each of the following examples:

use operator_sugar::*;
#[derive(Debug)] struct Left(i32);
#[derive(Debug)] struct Right(i32);
#[derive(Debug, Eq, PartialEq)] struct Answer(i32);

Addition

#
operator!(Left, Right: a + b -> Answer {
    Answer(a.0 + b.0)
});

fn main() {
    assert_eq!(Left(1) + Right(2), Answer(3));
}

Subtraction

#
operator!(Left, Right: a - b -> Answer {
    Answer(a.0 - b.0)
});

fn main() {
    assert_eq!(Left(1) - Right(2), Answer(-1));
}

Multiplication

#
operator!(Left, Right: a * b -> Answer {
    Answer(a.0 * b.0)
});

fn main() {
    assert_eq!(Left(3) * Right(2), Answer(6));
}

Division

#
operator!(Left, Right: a / b -> Answer {
    Answer(a.0 / b.0)
});

fn main() {
    assert_eq!(Left(8) / Right(2), Answer(4));
}

Remainder

#
operator!(Left, Right: a % b -> Answer {
    Answer(a.0 % b.0)
});

fn main() {
    assert_eq!(Left(9) % Right(5), Answer(4));
}

Bitwise AND

#
operator!(Left, Right: a & b -> Answer {
    Answer(a.0 & b.0)
});

fn main() {
    assert_eq!(Left(5) & Right(6), Answer(4));
}

Bitwise OR

#
operator!(Left, Right: a | b -> Answer {
    Answer(a.0 | b.0)
});

fn main() {
    assert_eq!(Left(5) | Right(6), Answer(7));
}

Bitwise XOR

#
operator!(Left, Right: a ^ b -> Answer {
    Answer(a.0 ^ b.0)
});

fn main() {
    assert_eq!(Left(5) ^ Right(6), Answer(3));
}

Shift-left

#
operator!(Left, Right: a << b -> Answer {
    Answer(a.0 << b.0)
});

fn main() {
    assert_eq!(Left(5) << Right(3), Answer(40));
}

Shift-right

#
operator!(Left, Right: a >> b -> Answer {
    Answer(a.0 >> b.0)
});

fn main() {
    assert_eq!(Left(43) >> Right(3), Answer(5));
}

Index

#[derive(Debug)] struct Left(Vec<i32>);
#[derive(Debug)] struct Right(usize);

operator!(Left, Right: a[b] -> &i32 {
    // The & is required to remind developers that a reference is to be returned.
    &a.0[b.0]
});

fn main() {
    // We check for 6 not &6, because while the impl returns &6, the [] operator from Rust dereferences it.
    assert_eq!(Left(vec![5, 6, 7])[Right(1)], 6);
}

Negative (-)


operator!(Left: -a -> Answer {
    Answer(-a.0)
});

fn main() {
    assert_eq!(-Left(43), Answer(-43));
}

Not (!)


operator!(Left: !a -> Answer {
    Answer(!a.0)
});

fn main() {
    assert_eq!(!Left(43), Answer(!43));
}

No runtime deps