ref_ops/
ref_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 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 A<T>"),
26            "where",
27            concat!("    &'a T: ", stringify!($Op), ","),
28            "{",
29            concat!("    type Output = A<<&'a 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>(a: T)",
37            "where",
38            concat!("    for<'a> &'a T: ", stringify!($Op), ","),
39            "{",
40            concat!("    let _op_a = (&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 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 &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>(a: T)",
71            "where",
72            concat!("    for<'a> &'a T: ", stringify!($Op), ","),
73            "{",
74            concat!("    let _op_a = (&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 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(&self) -> Self::Output;
95                );
96            }
97        );
98
99        impl<T, O> $op::Sealed for T
100        where
101            T: ?Sized,
102            for<'a> &'a T: $Op<Output = O>,
103        {
104        }
105
106        impl<T, O> $RefOp for T
107        where
108            T: ?Sized,
109            for<'a> &'a T: $Op<Output = O>,
110        {
111            type Output = O;
112
113            fn $ref_op(&self) -> O {
114                self.$op()
115            }
116        }
117    };
118}
119
120def_unary!(Neg, neg, RefNeg, ref_neg);
121def_unary!(Not, not, RefNot, ref_not);
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    macro_rules! test_unary {
128        ($fn:ident, $Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $assert:expr, $dummy:literal) => {
129            #[test]
130            fn $fn() {
131                #[derive(PartialEq)]
132                struct A<T: ?Sized>(T);
133
134                impl<T> $Op for &A<T>
135                where
136                    T: ?Sized + $RefOp,
137                {
138                    type Output = A<T::Output>;
139
140                    fn $op(self) -> Self::Output {
141                        A(self.0.$ref_op())
142                    }
143                }
144
145                fn f<T>(a: T)
146                where
147                    for<'a> &'a T: $Op,
148                {
149                    let _op_a = (&a).$op();
150
151                    // to do something with `a` and `_op_a`
152                }
153
154                fn g<T>(a: T)
155                where
156                    for<'a> &'a T: $Op,
157                {
158                    f(a);
159                }
160
161                g($dummy);
162
163                assert!($assert);
164            }
165        };
166    }
167
168    test_unary!(
169        test_neg,
170        Neg,
171        neg,
172        RefNeg,
173        ref_neg,
174        -&A(1.0) == A(-1.0),
175        1.0
176    );
177    test_unary!(
178        test_not,
179        Not,
180        not,
181        RefNot,
182        ref_not,
183        !&A(true) == A(false),
184        true
185    );
186}