forward_ref_generic/lib.rs
1//! This crate serves as a standalone extension of [forward_ref](https://crates.io/crates/forward_ref) to optionally support generics.
2//!
3//! The `forward_ref_*` macros are macros [used by the rust's core library](https://github.com/rust-lang/rust/blob/e7aca895980f25f6d2d3c48e10fd04656764d1e4/library/core/src/internal_macros.rs) to more easily implement Operations on primitive types.
4//! When implementing an operation `op` like [`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html) or [`Mul`](https://doc.rust-lang.org/std/ops/trait.Mul.html) for a type `T`, the [`std::ops` documentation](https://doc.rust-lang.org/std/ops) recommends implementing the operation not just for `T op T` but also for `T op &T`, `&T op T` and `&T op &T`.
5//! In practice, the implementations of those variants for `Copy` types is somewhat trivial and cumbersome to do.
6//! Since those trivial implementations are basically the same for all `Copy` types, one can use the `forward_ref_*` macros to get them implemented automatically.
7//!
8//! There are existing solutions for this (one of them the aforementioned [forward_ref](https://crates.io/crates/forward_ref) crate, as well as [impl_ops](https://crates.io/crates/impl_ops)), however none of them (or at least none I could find) support generic types.
9//! That is to say, if one has a type like `Point<T> {x: T, y: T}`, so far it was necessary to implement all variants by hand.
10//! 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 `Copy`.
11//!
12//! There are seperate macros offered for types of operations:
13//! * Unary Operators like [`Neg`](https://doc.rust-lang.org/std/ops/trait.Neg.html): [`forward_ref_unop`]
14//! * Binary Operators like [`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html): [`forward_ref_binop`]
15//! * Assignment Operators like [`AddAssign`](https://doc.rust-lang.org/std/ops/trait.AddAssign.html): [`forward_ref_op_assign`]
16//!
17//! # Examples
18//!
19//! ## `std::ops`'s `Point` example
20//!
21//! Let's use the [`std::ops`](https://doc.rust-lang.org/std/ops)'s `Point` example to see how one would usually implement this, and how it can instead be done using `forward_ref_*` macros:
22//! ```
23//! use std::ops::Add;
24//!
25//! #[derive(Debug, Copy, Clone, PartialEq)]
26//! struct Point {
27//! x: i32,
28//! y: i32,
29//! }
30//!
31//! impl Add for Point {
32//! type Output = Self;
33//!
34//! fn add(self, rhs: Self) -> Self::Output {
35//! Self {x: self.x + rhs.x, y: self.y + rhs.y}
36//! }
37//! }
38//! ```
39//!
40//! At this point, one can add two points together like so:
41//!
42//! ```
43//! # use std::ops::Add;
44//! #
45//! # #[derive(Debug, Copy, Clone, PartialEq)]
46//! # struct Point {
47//! # x: i32,
48//! # y: i32,
49//! # }
50//! #
51//! # impl Add for Point {
52//! # type Output = Self;
53//! #
54//! # fn add(self, rhs: Self) -> Self::Output {
55//! # Self {x: self.x + rhs.x, y: self.y + rhs.y}
56//! # }
57//! # }
58//! #
59//! let p1 = Point { x: 3, y: 3 };
60//! let p2 = Point { x: 5, y: 2 };
61//! assert_eq!(p1 + p2, Point { x: 8, y: 5 });
62//! ```
63//!
64//! However, using the operator on references will not compile:
65//!
66//! ```compile_fail
67//! # use std::ops::Add;
68//! #
69//! # #[derive(Debug, Copy, Clone, PartialEq)]
70//! # struct Point {
71//! # x: i32,
72//! # y: i32,
73//! # }
74//! #
75//! # impl Add for Point {
76//! # type Output = Self;
77//! #
78//! # fn add(self, rhs: Self) -> Self::Output {
79//! # Self {x: self.x + rhs.x, y: self.y + rhs.y}
80//! # }
81//! # }
82//! #
83//! # let p1 = Point { x: 3, y: 3 };
84//! # let p2 = Point { x: 5, y: 2 };
85//! let p3 = &p2;
86//! assert_eq!(p1 + &p2, Point { x: 8, y: 5 }); // ✖ does not compile
87//! assert_eq!(p1 + p3, Point { x: 8, y: 5 }); // ✖ does not compile
88//! assert_eq!(p1 + *p3, Point { x: 8, y: 5 }); // ✔ compiles
89//! ```
90//!
91//! To fix this, one would need to implement `Add<&Point>` for `Point`:
92//!
93//! ```
94//! # use std::ops::Add;
95//! #
96//! # #[derive(Debug, Copy, Clone, PartialEq)]
97//! # struct Point {
98//! # x: i32,
99//! # y: i32,
100//! # }
101//! #
102//! # impl Add for Point {
103//! # type Output = Self;
104//! #
105//! # fn add(self, rhs: Self) -> Self::Output {
106//! # Self {x: self.x + rhs.x, y: self.y + rhs.y}
107//! # }
108//! # }
109//! #
110//! impl Add<&Point> for Point {
111//! type Output = Self;
112//!
113//! fn add(self, rhs: &Self) -> Self::Output {
114//! Self::add(self, *rhs)
115//! }
116//! }
117//!
118//! # let p1 = Point { x: 3, y: 3 };
119//! # let p2 = Point { x: 5, y: 2 };
120//! let p3 = &p2;
121//! assert_eq!(p1 + &p2, Point { x: 8, y: 5 });
122//! assert_eq!(p1 + p3, Point { x: 8, y: 5 });
123//! ```
124//!
125//! And now we would have to add implementations for `&Point + Point` and `&Point + &Point` as well.
126//! But that is very verbose and annoying to do.
127//! Instead, we can use [`forward_ref_binop`](https://docs.rs/forward_ref_generic/*/forward_ref_generic/macro.forward_ref_binop.html):
128//!
129//! ```
130//! # use std::ops::Add;
131//! #
132//! # #[derive(Debug, Copy, Clone, PartialEq)]
133//! # struct Point {
134//! # x: i32,
135//! # y: i32,
136//! # }
137//! #
138//! # impl Add for Point {
139//! # type Output = Self;
140//! #
141//! # fn add(self, rhs: Self) -> Self::Output {
142//! # Self {x: self.x + rhs.x, y: self.y + rhs.y}
143//! # }
144//! # }
145//! #
146//! use forward_ref_generic::forward_ref_binop;
147//!
148//! forward_ref_binop! {
149//! impl Add for Point
150//! }
151//!
152//! let p1 = Point { x: 3, y: 3 };
153//! let p2 = Point { x: 5, y: 2 };
154//! assert_eq!(p1 + p2, Point { x: 8, y: 5 });
155//! assert_eq!(p1 + &p2, Point { x: 8, y: 5 });
156//! assert_eq!(&p1 + p2, Point { x: 8, y: 5 });
157//! assert_eq!(&p1 + &p2, Point { x: 8, y: 5 });
158//! ```
159//!
160//! ## Support for generics
161//!
162//! Let's generalize our `Point` struct so that it supports members of any type.
163//! We can still use [`forward_ref_binop`](https://docs.rs/forward_ref_generic/*/forward_ref_generic/macro.forward_ref_binop.html) in that case, but we'll need to tell the macro which generics we used.
164//! We will also need to tell it all trait bounds that are required.
165//! Note that, for technical reasons, we'll need to add angled brackets `[]` around the list of generics.
166//!
167//! ```
168//! use std::ops::Add;
169//! use forward_ref_generic::forward_ref_binop;
170//!
171//! #[derive(Debug, Copy, Clone, PartialEq)]
172//! struct Point<T> {
173//! x: T,
174//! y: T,
175//! }
176//!
177//! impl<T> Add for Point<T>
178//! where
179//! T: Copy + Add<Output = T>,
180//! {
181//! type Output = Self;
182//!
183//! fn add(self, rhs: Self) -> Self::Output {
184//! Self {x: self.x + rhs.x, y: self.y + rhs.y}
185//! }
186//! }
187//!
188//! // for the exact syntax required by each macro, refer to the macro's documentation page
189//! forward_ref_binop! {
190//! [T]
191//! impl Add for Point<T>
192//! where T: Copy + Add<Output = T>
193//! }
194//!
195//! let p1 = Point { x: 3, y: 3 };
196//! let p2 = Point { x: 5, y: 2 };
197//! assert_eq!(p1 + p2, Point { x: 8, y: 5 });
198//! assert_eq!(p1 + &p2, Point { x: 8, y: 5 });
199//! assert_eq!(&p1 + p2, Point { x: 8, y: 5 });
200//! assert_eq!(&p1 + &p2, Point { x: 8, y: 5 });
201//! ```
202//!
203//! ## Const generics and different RHS
204//!
205//! So far, the right hand side of the operation was of the same type as the left hand side.
206//! But `forward_ref_*` macros also optionally support defining a different right hand side.
207//! To do so, simply add the RHS-type right after the LHS type like so:
208//!
209//! ```ignore
210//! forward_ref_binop! {
211//! [generics...]
212//! impl OP for LHS, RHS
213//! where ...
214//! }
215//! ```
216//!
217//! To demonstrate this in action, we'll use a generic Stack-Matrix and implement [`Mul`](https://doc.rust-lang.org/std/ops/trait.Mul.html) on it:
218//!
219//! ```
220//! use std::ops::{Add, Mul};
221//! use forward_ref_generic::forward_ref_binop;
222//!
223//! #[derive(Debug, Copy, Clone, PartialEq)]
224//! struct Matrix<T, const M: usize, const N: usize> {
225//! m: [[T; N]; M],
226//! }
227//!
228//! # impl<T, const M: usize, const N: usize> Matrix<T, M, N>
229//! # where
230//! # T: Copy,
231//! # {
232//! # pub fn transposed(self) -> Matrix<T, N, M> {
233//! # let mut result = [[None; M]; N];
234//! # for i in 0..M {
235//! # for j in 0..N {
236//! # result[j][i] = Some(self.m[i][j]);
237//! # }
238//! # }
239//! # Matrix {
240//! # m: result.map(|x| x.map(|x| x.unwrap())),
241//! # }
242//! # }
243//! # }
244//! #
245//! impl<T, const M: usize, const N: usize, const L: usize> Mul<Matrix<T, N, L>> for Matrix<T, M, N>
246//! where
247//! T: Copy + Add<Output = T> + Mul<Output = T>,
248//! {
249//! type Output = Matrix<T, M, L>;
250//!
251//! fn mul(self, rhs: Matrix<T, N, L>) -> Self::Output {
252//! # // this is not a good implementation of mul
253//! # // as it will panic if some of the const generics are 0
254//! # // it's potentially better to use T::default here
255//! # // but as this is just a doc example, it doesn't matter
256//! # let other_transposed = rhs.transposed();
257//! # let mut result = [[None; L]; M];
258//! # for i in 0..M {
259//! # for j in 0..L {
260//! # if let Some(val) = self.m[i]
261//! # .into_iter()
262//! # .zip(other_transposed.m[j])
263//! # .map(|(x1, x2)| x1 * x2)
264//! # .reduce(|acc, x| acc + x)
265//! # {
266//! # result[i][j] = Some(val);
267//! # }
268//! # }
269//! # }
270//! # Matrix {
271//! # m: result.map(|x| x.map(|x| x.unwrap())),
272//! # }
273//! // ...
274//! }
275//! }
276//!
277//! forward_ref_binop! {
278//! [T, const M: usize, const N: usize, const L: usize]
279//! impl Mul for Matrix<T, M, N>, Matrix<T, N, L>
280//! where T: Copy + Add<Output = T> + Mul<Output = T>
281//! }
282//!
283//! let m1 = Matrix {m: [[1, 2, 2], [2, 1, 2]]};
284//! let m2 = Matrix {m: [[0, 1], [1, 1], [2, 1]]};
285//!
286//! assert_eq!(m1 * m2, Matrix {m: [[6, 5], [5, 5]]});
287//! assert_eq!(m1 * &m2, Matrix {m: [[6, 5], [5, 5]]});
288//! assert_eq!(&m1 * m2, Matrix {m: [[6, 5], [5, 5]]});
289//! assert_eq!(&m1 * &m2, Matrix {m: [[6, 5], [5, 5]]});
290//! ```
291//!
292//! ## Custom operators
293//!
294//! Notice that in all previous examples, all information the macro required on *which* operation is supposed to be implemented was the Trait's name.
295//! This is done by specifically checking for known Operator Traits and inserting the required method's name from inside the macro.
296//! This is currently **only** done for standard mathematical operators (i.e. not for bitwise operators and not for custom operators).
297//! 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:
298//!
299//! ```ignore
300//! forward_ref_binop! {
301//! [generics...]
302//! impl OP, METHOD for LHS, RHS
303//! where ...
304//! }
305//! ```
306//!
307//! To demonstrate, we will implement the [`Not`](https://doc.rust-lang.org/std/ops/trait.Not.html) unary operator on the [`std::ops::Not`](https://doc.rust-lang.org/std/ops/trait.Not.html)'s doc's `Answer` example:
308//!
309//! ```
310//! use std::ops::Not;
311//! use forward_ref_generic::forward_ref_unop;
312//!
313//! // notice we have to add the `Copy` trait, as otherwise the macro will not work correctly
314//! #[derive(Debug, Copy, Clone, PartialEq)]
315//! enum Answer {
316//! Yes,
317//! No,
318//! }
319//!
320//! impl Not for Answer {
321//! type Output = Self;
322//!
323//! fn not(self) -> Self::Output {
324//! match self {
325//! Answer::Yes => Answer::No,
326//! Answer::No => Answer::Yes,
327//! }
328//! }
329//! }
330//!
331//! // this time we use the macro for unary operators and specify the `not` method's name
332//! forward_ref_unop! {
333//! impl Not, not for Answer
334//! }
335//!
336//! assert_eq!(!Answer::Yes, Answer::No);
337//! assert_eq!(!Answer::No, Answer::Yes);
338//!
339//! assert_eq!(!&Answer::Yes, Answer::No);
340//! assert_eq!(!&Answer::No, Answer::Yes);
341//! ```
342//!
343//! ### Making an operation commutative
344//!
345//! There are also macros to automatically make an operation commutative. That is, for two types `T` and `U`, if `T binop U` is implemented, then one can use [`commutative_binop`] to automatically implement `U binop T`. If `T` and `U` are additionally `Copy`, then `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`].
346//!
347//! ```
348//! use std::ops::Add;
349//! use forward_ref_generic::{commutative_binop, forward_ref_commutative_binop};
350//!
351//! // two wrappers for integers
352//! #[derive(Clone, Copy, PartialEq)]
353//! struct Int1(i32);
354//!
355//! #[derive(Clone, Copy, PartialEq)]
356//! struct Int2(i32);
357//!
358//! impl Add<Int2> for Int1 {
359//! type Output = i32;
360//!
361//! fn add(self, rhs: Int2) -> Self::Output {
362//! self.0 + rhs.0
363//! }
364//! }
365//!
366//! // note that the order of `LHS` and `RHS` is that
367//! // of the original operation's implementation
368//! // not that of the created one
369//! commutative_binop! {
370//! impl Add for Int1, Int2
371//! }
372//!
373//! // the order of `LHS` and `RHS` here doesn't matter
374//! // as `LHS binop RHS` and `RHS binop LHS` are both required anyway
375//! forward_ref_commutative_binop! {
376//! impl Add for Int1, Int2
377//! }
378//!
379//! let i1 = Int1(5);
380//! let i2 = Int2(3);
381//!
382//! assert_eq!(i1 + i2, 8);
383//! assert_eq!(i2 + i1, 8);
384//!
385//! assert_eq!(&i1 + i2, 8);
386//! assert_eq!(i1 + &i2, 8);
387//! assert_eq!(&i1 + &i2, 8);
388//!
389//! assert_eq!(&i2 + i1, 8);
390//! assert_eq!(i2 + &i1, 8);
391//! assert_eq!(&i2 + &i1, 8);
392//! ```
393
394mod assignment;
395mod binary;
396mod unary;