ref_ops/
ref_mut_unary.rs

1use core::ops::{Neg, Not};
2
3macro_rules! doc {
4    ($( $x:expr, )* @$item:item) => {
5        $( #[doc = $x] )*
6        $item
7    };
8}
9
10macro_rules! def_unary {
11    ($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident) => {
12        mod $op {
13            pub trait Sealed {}
14        }
15
16        doc!(
17            concat!("`", stringify!($op), "` operation through mutable references."),
18            "",
19            "As of Rust 1.73.0, the following code does not compile:",
20            "```compile_fail",
21            concat!("use core::ops::", stringify!($Op), ";"),
22            "",
23            "struct A<T>(T);",
24            "",
25            concat!("impl<'a, T> ", stringify!($Op), " for &'a mut A<T>"),
26            "where",
27            concat!("    &'a mut T: ", stringify!($Op), ","),
28            "{",
29            concat!("    type Output = A<<&'a mut T as ", stringify!($Op), ">::Output>;"),
30            "",
31            concat!("    fn ", stringify!($op), "(self) -> Self::Output {"),
32            concat!("        A(self.0.", stringify!($op), "())"),
33            "    }",
34            "}",
35            "",
36            "fn _f<T>(mut a: T)",
37            "where",
38            concat!("    for<'a> &'a mut T: ", stringify!($Op), ","),
39            "{",
40            concat!("    let _op_a = (&mut a).", stringify!($op), "();"),
41            "",
42            concat!("    // to do something with `a` and `_op_a`"),
43            "}",
44            "",
45            "fn _g<T>(a: T)",
46            "where",
47            concat!("    for<'a> &'a mut T: ", stringify!($Op), ","),
48            "{",
49            "    _f(a);",
50            "}",
51            "```",
52            "but the following code does:",
53            "```",
54            concat!("use core::ops::", stringify!($Op), ";"),
55            concat!("use ref_ops::", stringify!($RefOp),";"),
56            "",
57            "struct A<T>(T);",
58            "",            "",
59            concat!("impl<T> ", stringify!($Op), " for &mut A<T>"),
60            "where",
61            concat!("    T: ", stringify!($RefOp), ","),
62            "{",
63            "    type Output = A<T::Output>;",
64            "",
65            concat!("    fn ", stringify!($op), "(self) -> Self::Output {"),
66            concat!("        A(self.0.", stringify!($ref_op), "())"),
67            "    }",
68            "}",
69            "",
70            "fn _f<T>(mut a: T)",
71            "where",
72            concat!("    for<'a> &'a mut T: ", stringify!($Op), ","),
73            "{",
74            concat!("    let _op_a = (&mut a).", stringify!($op), "();"),
75            "",
76            concat!("    // to do something with `a` and `_op_a`"),
77            "}",
78            "",
79            "fn _g<T>(a: T)",
80            "where",
81            concat!("    for<'a> &'a mut T: ", stringify!($Op), ","),
82            "{",
83            "    _f(a);",
84            "}",
85            "```",
86            @pub trait $RefOp: $op::Sealed {
87                doc!(
88                    concat!("The resulting type after applying `", stringify!($op), "` operation."),
89                    @type Output;
90                );
91
92                doc!(
93                    concat!("Performs `", stringify!($op), "` operation."),
94                    @fn $ref_op(&mut self) -> Self::Output;
95                );
96            }
97        );
98
99        impl<T, O> $op::Sealed for T
100        where
101            T: ?Sized,
102            for<'a> &'a mut T: $Op<Output = O>,
103        {
104        }
105
106        impl<T, O> $RefOp for T
107        where
108            T: ?Sized,
109            for<'a> &'a mut T: $Op<Output = O>,
110        {
111            type Output = O;
112
113            fn $ref_op(&mut self) -> O {
114                self.$op()
115            }
116        }
117    };
118}
119
120def_unary!(Neg, neg, RefMutNeg, ref_mut_neg);
121def_unary!(Not, not, RefMutNot, ref_mut_not);
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use crate::{RefNeg, RefNot};
127
128    #[derive(PartialEq)]
129    struct B<T>(T);
130
131    macro_rules! impl_unary {
132        ($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident) => {
133            impl<T> $Op for &mut B<T>
134            where
135                T: $RefOp,
136            {
137                type Output = B<T::Output>;
138
139                fn $op(self) -> Self::Output {
140                    B(self.0.$ref_op())
141                }
142            }
143        };
144    }
145
146    impl_unary!(Neg, neg, RefNeg, ref_neg);
147    impl_unary!(Not, not, RefNot, ref_not);
148
149    macro_rules! test_unary {
150        ($fn:ident, $Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $assert:expr, $dummy:expr) => {
151            #[test]
152            fn $fn() {
153                #[derive(PartialEq)]
154                struct A<T: ?Sized>(T);
155
156                impl<T> $Op for &mut A<T>
157                where
158                    T: ?Sized + $RefOp,
159                {
160                    type Output = A<T::Output>;
161
162                    fn $op(self) -> Self::Output {
163                        A(self.0.$ref_op())
164                    }
165                }
166
167                fn f<T>(mut a: T)
168                where
169                    for<'a> &'a mut T: $Op,
170                {
171                    let _op_a = (&mut a).$op();
172
173                    // to do something with `a` and `_op_a`
174                }
175
176                fn g<T>(a: T)
177                where
178                    for<'a> &'a mut T: $Op,
179                {
180                    f(a);
181                }
182
183                g($dummy);
184
185                assert!($assert);
186            }
187        };
188    }
189
190    test_unary!(
191        test_neg,
192        Neg,
193        neg,
194        RefMutNeg,
195        ref_mut_neg,
196        -&mut A(B(1.0)) == A(B(-1.0)),
197        B(1.0)
198    );
199    test_unary!(
200        test_not,
201        Not,
202        not,
203        RefMutNot,
204        ref_mut_not,
205        !&mut A(B(true)) == A(B(false)),
206        B(true)
207    );
208}