use core::ops::{Neg, Not};
macro_rules! doc {
($( $x:expr, )* @$item:item) => {
$( #[doc = $x] )*
$item
};
}
macro_rules! def_unary {
($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident) => {
mod $op {
pub trait Sealed {}
}
doc!(
concat!("`", stringify!($op), "` operation through mutable references."),
"",
"As of Rust 1.72.1, the following code does not compile:",
"```compile_fail",
concat!("use core::ops::", stringify!($Op), ";"),
"",
"struct A<T>(T);",
"",
concat!("impl<'a, T, O> ", stringify!($Op), " for &'a mut A<T>"),
"where",
concat!(" &'a mut T: ", stringify!($Op), "<Output = O>,"),
"{",
concat!(" type Output = A<O>;"),
"",
concat!(" fn ", stringify!($op), "(self) -> Self::Output {"),
concat!(" A(self.0.", stringify!($op), "())"),
" }",
"}",
"",
"fn _f<T>(mut a: T)",
"where",
concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
"{",
concat!(" let _op_a = (&mut a).", stringify!($op), "();"),
"",
concat!(" // to do something with `a` and `_op_a`"),
"}",
"",
"fn _g<T>(a: T)",
"where",
concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
"{",
" _f(a);",
"}",
"```",
"but the following code does:",
"```",
concat!("use core::ops::", stringify!($Op), ";"),
concat!("use ref_ops::", stringify!($RefOp),";"),
"",
"struct A<T>(T);",
"", "",
concat!("impl<T> ", stringify!($Op), " for &mut A<T>"),
"where",
concat!(" T: ", stringify!($RefOp), ","),
"{",
" type Output = A<T::Output>;",
"",
concat!(" fn ", stringify!($op), "(self) -> Self::Output {"),
concat!(" A(self.0.", stringify!($ref_op), "())"),
" }",
"}",
"",
"fn _f<T>(mut a: T)",
"where",
concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
"{",
concat!(" let _op_a = (&mut a).", stringify!($op), "();"),
"",
concat!(" // to do something with `a` and `_op_a`"),
"}",
"",
"fn _g<T>(a: T)",
"where",
concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
"{",
" _f(a);",
"}",
"```",
@pub trait $RefOp: $op::Sealed {
doc!(
concat!("The resulting type after applying `", stringify!($op), "` operation."),
@type Output;
);
doc!(
concat!("Performs `", stringify!($op), "` operation."),
@fn $ref_op(&mut self) -> Self::Output;
);
}
);
impl<T, O> $op::Sealed for T
where
T: ?Sized,
for<'a> &'a mut T: $Op<Output = O>,
{
}
impl<T, O> $RefOp for T
where
T: ?Sized,
for<'a> &'a mut T: $Op<Output = O>,
{
type Output = O;
fn $ref_op(&mut self) -> O {
self.$op()
}
}
};
}
def_unary!(Neg, neg, RefMutNeg, ref_mut_neg);
def_unary!(Not, not, RefMutNot, ref_mut_not);
#[cfg(test)]
mod tests {
use super::*;
use crate::{RefNeg, RefNot};
#[derive(PartialEq)]
struct B<T>(T);
macro_rules! impl_unary {
($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident) => {
impl<T> $Op for &mut B<T>
where
T: $RefOp,
{
type Output = B<T::Output>;
fn $op(self) -> Self::Output {
B(self.0.$ref_op())
}
}
};
}
impl_unary!(Neg, neg, RefNeg, ref_neg);
impl_unary!(Not, not, RefNot, ref_not);
macro_rules! test_unary {
($fn:ident, $Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $assert:expr, $dummy:expr) => {
#[test]
fn $fn() {
#[derive(PartialEq)]
struct A<T: ?Sized>(T);
impl<T> $Op for &mut A<T>
where
T: ?Sized + $RefOp,
{
type Output = A<T::Output>;
fn $op(self) -> Self::Output {
A(self.0.$ref_op())
}
}
fn f<T>(mut a: T)
where
for<'a> &'a mut T: $Op,
{
let _op_a = (&mut a).$op();
}
fn g<T>(a: T)
where
for<'a> &'a mut T: $Op,
{
f(a);
}
g($dummy);
assert!($assert);
}
};
}
test_unary!(
test_neg,
Neg,
neg,
RefMutNeg,
ref_mut_neg,
-&mut A(B(1.0)) == A(B(-1.0)),
B(1.0)
);
test_unary!(
test_not,
Not,
not,
RefMutNot,
ref_mut_not,
!&mut A(B(true)) == A(B(false)),
B(true)
);
}