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;