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
9KB
67 lines
xops
Rust procedural macros for overloading operators.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
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>
forA
, we are essentially sayingA
is capable of addition withB
, and this is subtlety distinct fromB
is capable of addition withA
, which we would achieve by implementingAdd<A>
forB
. In mathematics, on the other hand, it would be more common to say something like addition is defined betweenA
andB
; 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