### 8 releases

0.2.1 | Feb 12, 2022 |
---|---|

0.2.0 | Feb 12, 2022 |

0.1.5 | Feb 10, 2022 |

#**1594** in Rust patterns

**148** downloads per month

**MIT**license

40KB

276 lines

`forward_ref_generic`

`forward_ref_generic`

This crate serves as a standalone extension of forward_ref to optionally support generics.

The

macros are macros used by the rust's core library to more easily implement Operations on primitive types.
When implementing an operation `forward_ref_ *`

`op`

like `Add`

or `Mul`

for a type `T`

, the `std``::`ops

documentation recommends implementing the operation not just for `T op T`

but also for `T op ``&`T

, `&`T op T

and `&`T op `&`T

.
In practice, the implementations of those variants for `Copy`

types is somewhat trivial and cumbersome to do.
Since those trivial implementations are basically the same for all `Copy`

types, one can use the `forward_ref_``*`

macros to get them implemented automatically.There are existing solutions for this (one of them the aforementioned forward_ref crate, as well as impl_ops), however none of them (or at least none I could find) support generic types.
That is to say, if one has a type like

, so far it was necessary to implement all variants by hand.
This crate offers macros that also support generic types, including trait bounds, so the only assumption left is that the type the operation is implemented on is `Point <T>`

`{`x`:`T`,`y`:`T

`}``Copy`

.There are seperate macros offered for types of operations:

- Unary Operators like

:`Neg``forward_ref_unop` - Binary Operators like

:`Add``forward_ref_binop` - Assignment Operators like

:`AddAssign``forward_ref_op_assign`

## Usage

Add this to your

:`Cargo .toml`

`[``dependencies``]`
`forward_ref_generic ``=` `"`0.2`"`

For usage of the macros, refer to the documentation or see the examples below.

## Examples

`std``::`ops

's `Point`

example

`std`ops`::``Point`

Let's use the

's `std ::`ops

`Point`

example to see how one would usually implement this, and how it can instead be done using `forward_ref_``*`

macros:`use` `std``::``ops``::`Add`;`
`#``[``derive``(``Debug``,` Copy`,` Clone`,` PartialEq`)``]`
`struct` `Point` `{`
`x``:` `i32`,
`y``:` `i32`,
`}`
`impl` `Add ``for`` ``Point` `{`
`type` `Output` `=` `Self``;`
`fn` `add``(``self`, `rhs``:` `Self``)`` ``->` `Self``::`Output `{`
`Self` `{`x`:` `self``.`x `+` rhs`.`x`,` y`:` `self``.`y `+` rhs`.`y`}`
`}`
`}`

At this point, one can add two points together like so:

`let` p1 `=` Point `{` x`:` `3``,` y`:` `3` `}``;`
`let` p2 `=` Point `{` x`:` `5``,` y`:` `2` `}``;`
`assert_eq!``(`p1 `+` p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`

However, using the operator on references will not compile:

`let` p3 `=` `&`p2`;`
`assert_eq!``(`p1 `+` `&`p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;` `//` ✖ does not compile
`assert_eq!``(`p1 `+` p3`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;` `//` ✖ does not compile
`assert_eq!``(`p1 `+` `*`p3`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;` `//` ✔ compiles

To fix this, one would need to implement

for `Add <&Point>`

`Point`

:`impl` `Add``<``&`Point`>` `for`` ``Point` `{`
`type` `Output` `=` `Self``;`
`fn` `add``(``self`, `rhs``:` `&``Self``)`` ``->` `Self``::`Output `{`
`Self``::`add`(``self``,` `*`rhs`)`
`}`
`}`
`let` p3 `=` `&`p2`;`
`assert_eq!``(`p1 `+` `&`p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(`p1 `+` p3`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`

And now we would have to add implementations for

and `&`Point `+` Point

as well.
But that is very verbose and annoying to do.
Instead, we can use `&`Point `+` `&`Point

:`forward_ref_binop`

`use` `forward_ref_generic``::`forward_ref_binop`;`
`forward_ref_binop!` `{`
`impl` `Add ``for`` ``Point`
}
`let` `p1` = `Point` `{` x`:` `3``,` y`:` `3` `}``;`
`let` p2 `=` Point `{` x`:` `5``,` y`:` `2` `}``;`
`assert_eq!``(`p1 `+` p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(`p1 `+` `&`p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(``&`p1 `+` p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(``&`p1 `+` `&`p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`

### Support for generics

Let's generalize our

struct so that it supports members of any type.
We can still use `Point`

in that case, but we'll need to tell the macro which generics we used.
We will also need to tell it all trait bounds that are required.
Note that, for technical reasons, we'll need to add angled brackets `forward_ref_binop`

around the list of generics.`[``]`

`use` `std``::``ops``::`Add`;`
`use` `forward_ref_generic``::`forward_ref_binop`;`
`#``[``derive``(``Debug``,` Copy`,` Clone`,` PartialEq`)``]`
`struct` `Point``<`T`>`` ``{`
`x``:` T,
`y``:` T,
`}`
`impl``<`T`>`` Add ``for`` ``Point``<`T`>`
`where`
T`:` Copy + `Add``<`Output = T`>`,
`{`
`type` `Output` `=` `Self``;`
`fn` `add``(``self`, `rhs``:` `Self``)`` ``->` `Self``::`Output `{`
`Self` `{`x`:` `self``.`x `+` rhs`.`x`,` y`:` `self``.`y `+` rhs`.`y`}`
`}`
`}`
`//` for the exact syntax required by each macro, refer to the macro's documentation page
`forward_ref_binop!` `{`
`[`T`]`
`impl` `Add ``for`` ``Point``<`T`>`
`where` T`:` Copy + `Add``<`Output = T`>`
}
let p1 = Point `{` x`:` `3``,` y`:` `3` `}``;`
`let` p2 `=` Point `{` x`:` `5``,` y`:` `2` `}``;`
`assert_eq!``(`p1 `+` p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(`p1 `+` `&`p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(``&`p1 `+` p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`
`assert_eq!``(``&`p1 `+` `&`p2`,` Point `{` x`:` `8``,` y`:` `5` `}``)``;`

### Const generics and different RHS

So far, the right hand side of the operation was of the same type as the left hand side.
But

macros also optionally support defining a different right hand side.
To do so, simply add the RHS-type right after the LHS type like so:`forward_ref_ *`

`forward_ref_binop!` `{`
`[`generics`...``]`
`impl` `OP ``for`` ``LHS`, `RHS`
`where` ...
}

To demonstrate this in action, we'll use a generic Stack-Matrix and implement

on it:`Mul`

`use` `std``::``ops``::``{`Add`,` Mul`}``;`
`use` `forward_ref_generic``::`forward_ref_binop`;`
`#``[``derive``(``Debug``,` Copy`,` Clone`,` PartialEq`)``]`
`struct` `Matrix``<`T, `const` M`:` `usize`, `const` N`:` `usize``>`` ``{`
`m``:` [[T; N]; M],
`}`
`impl``<`T, `const` M`:` `usize`, `const` N`:` `usize`, `const` L`:` `usize``>`` ``Mul``<`Matrix`<`T, N, L`>``>` `for`` ``Matrix``<`T, M, N`>`
`where`
T`:` Copy + `Add``<`Output = T`>` + `Mul``<`Output = T`>`,
`{`
`type` `Output` `=` `Matrix``<`T, M, L`>``;`
`fn` `mul``(``self`, `rhs``:` `Matrix``<`T, N, L`>``)`` ``->` `Self``::`Output `{`
`//` ...
`}`
`}`
`forward_ref_binop!` `{`
`[`T`,` `const` M`:` `usize``,` `const` N`:` `usize``,` `const` L`:` `usize``]`
`impl` `Mul ``for`` ``Matrix``<`T, M, N`>`, `Matrix``<`T, N, L`>`
`where` T`:` Copy + `Add``<`Output = T`>` + `Mul``<`Output = T`>`
}
let m1 = Matrix `{`m`:` `[``[``1``,` `2``,` `2``]``,` `[``2``,` `1``,` `2``]``]``}``;`
`let` m2 `=` Matrix `{`m`:` `[``[``0``,` `1``]``,` `[``1``,` `1``]``,` `[``2``,` `1``]``]``}``;`
`assert_eq!``(`m1 `*` m2`,` Matrix `{`m`:` `[``[``6``,` `5``]``,` `[``5``,` `5``]``]``}``)``;`
`assert_eq!``(`m1 `*` `&`m2`,` Matrix `{`m`:` `[``[``6``,` `5``]``,` `[``5``,` `5``]``]``}``)``;`
`assert_eq!``(``&`m1 `*` m2`,` Matrix `{`m`:` `[``[``6``,` `5``]``,` `[``5``,` `5``]``]``}``)``;`
`assert_eq!``(``&`m1 `*` `&`m2`,` Matrix `{`m`:` `[``[``6``,` `5``]``,` `[``5``,` `5``]``]``}``)``;`

### Custom operators

Notice that in all previous examples, all information the macro required on *which* operation is supposed to be implemented was the Trait's name.
This is done by specifically checking for known Operator Traits and inserting the required method's name from inside the macro.
This is currently **only** done for standard mathematical operators (i.e. not for bitwise operators and not for custom operators).
However, one can still use the macros, but the method's name has to be specified in that case. RHS can again be omitted if LHS = RHS:

`forward_ref_binop!` `{`
`[`generics`...``]`
`impl` `OP`, `METHOD` `for` `LHS`, `RHS`
`where` ...
}

To demonstrate, we will implement the

unary operator on the `Not`

's doc's `std ::`

`ops`Not

`::``Answer`

example:`use` `std``::``ops``::`Not`;`
`use` `forward_ref_generic``::`forward_ref_unop`;`
`//` notice we have to add the `Copy` trait, as otherwise the macro will not work correctly
`#``[``derive``(``Debug``,` Copy`,` Clone`,` PartialEq`)``]`
`enum` `Answer` `{`
Yes`,`
No`,`
`}`
`impl` `Not ``for`` ``Answer` `{`
`type` `Output` `=` `Self``;`
`fn` `not``(``self``)`` ``->` `Self``::`Output `{`
`match` `self` `{`
`Answer``::`Yes `=>` `Answer``::`No`,`
`Answer``::`No `=>` `Answer``::`Yes`,`
`}`
`}`
`}`
`//` this time we use the macro for unary operators and specify the `not` method's name
`forward_ref_unop!` `{`
`impl` `Not`, `not` `for` `Answer`
}
`assert_eq`!(!`Answer`::`Yes`, `Answer`::`No`);
`assert_eq`!(!`Answer`::`No`, `Answer`::`Yes`);
`assert_eq`!(!`&``Answer`::`Yes`, `Answer`::`No`);
`assert_eq`!(!`&``Answer`::`No`, `Answer`::`Yes`);

### Making an operation commutative

There are also macros to automatically make an operation commutative. That is, for two types

and `T`

, if `U`

is implemented, then one can use `T binop U`

to automatically implement `commutative_binop`

. If `U binop T`

and `T`

are additionally `U`

, then `Copy`

, `T binop &U`

`&`T binop U

, `&`T binop `&`U

, `U binop ``&`T

, `&`U binop T

and `&`U binop `&`T

can automatically be implemented with `forward_ref_commutative_binop`

.`use` `std``::``ops``::`Add`;`
`use` `forward_ref_generic``::``{`commutative_binop`,` forward_ref_commutative_binop`}``;`
`//` two wrappers for integers
`#``[``derive``(``Clone``,` Copy`,` PartialEq`)``]`
`struct` `Int1``(``i32``)``;`
`#``[``derive``(``Clone``,` Copy`,` PartialEq`)``]`
`struct` `Int2``(``i32``)``;`
`impl` `Add``<`Int2`>` `for`` ``Int1` `{`
`type` `Output` `=` `i32``;`
`fn` `add``(``self`, `rhs``:` Int2`)`` ``->` `Self``::`Output `{`
`self``.``0` `+` rhs`.``0`
`}`
`}`
`//` note that the order of `LHS` and `RHS` is that
`//` of the original operation's implementation
`//` not that of the created one
`commutative_binop!` `{`
`impl` `Add ``for`` ``Int1`, `Int2`
}
`//` the order of `LHS` and `RHS` here doesn't matter
`//` as `LHS binop RHS` and `RHS binop LHS` are both required anyway
`forward_ref_commutative_binop`! `{`
`impl` Add `for` `Int1`, `Int2`
}
`let` `i1` = `Int1`(5);
`let` `i2` = `Int2`(3);
`assert_eq`!(`i1` + `i2`, 8);
`assert_eq`!(`i2` + `i1`, 8);
`assert_eq`!(`&``i1` + `i2`, 8);
`assert_eq`!(`i1` + `&``i2`, 8);
`assert_eq`!(`&``i1` + `&``i2`, 8);
`assert_eq`!(`&``i2` + `i1`, 8);
`assert_eq`!(`i2` + `&``i1`, 8);
`assert_eq`!(`&``i2` + `&``i1`, 8);