2 releases

0.1.1 Sep 5, 2021
0.1.0 Sep 1, 2021

#15 in #operator-overloading

30 downloads per month
Used in algeo

MIT/Apache

9KB
67 lines

xops

Crate API

Rust procedural macros for overloading operators.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


lib.rs:

Procedural macros to help with overloading operators.

About

xops = eX(tra/tended/cellent) + OPerat(or/ion) + S

This crate provides macros which implement families of operations related to a given operation implementation.

Usage/Examples

See binop

Operator Overloading Basics

All the traits for overloading operators in std::ops follow a common pattern. Take, for example, the Add trait for overloading the + operator; its trait definition looks like this:

trait Add<Rhs = Self> {
    type Output;
    fn add(self, rhs: Rhs) -> Self::Output;
}

The receiving type Self is the left-hand side of the + operator and the generic type argument Rhs is right-hand side. Implementing Add<B> for A will then make the expression a + b equivalent to <A as Add<B>>::add(a, b) , for any a: A and b: B.

For examples of implementations of these traits, see std::ops.

Discussion

The functionality of xops is very much like other 'derive' crates, automatically deriving trait implementations. However, instead of the attributes being placed on a struct, enum, or union (as with the derive attribute), the attributes in xops are placed on trait implementations, i.e., items of the form impl Trait for Type { ... }.

The reasoning behind putting the attributes on trait implementations has both pragmatic and semantic components:

  • From a trait implementation, xops is able to directly parse all of the information it needs to do its job, namely type, trait, and method identifiers. If xops used derive macros, all this information would either need to be given by the user or xops would need some sort of catalogue about all the standard library operations. With the current approach, however, you are not even limited to just the standard library operations, xops will work on any traits with the same sort of layout. (Although, Rust does not support custom operation overloading, so there probably isn't much use outside the standard library operations).

  • In Rust, when we implement Add<B> for A, we are essentially saying A is capable of addition with B, and this is subtlety distinct from B is capable of addition with A, which we would achieve by implementing Add<A> for B. In mathematics, on the other hand, it would be more common to say something like addition is defined between A and B; the difference being that the operation itself is treated as more of a first-class citizen. The approach of xops is in agreement with the latter interpretation, to the extent that Rust permits.

Dependencies

~2MB
~45K SLoC