#![cfg_attr(feature = "debug-trace-macros", feature(trace_macros))]
#[cfg(feature = "debug-trace-macros")] trace_macros!(true);
#[macro_export(local_inner_macros)]
macro_rules! newtype_ops {
($($rest:tt)*) => { newtype_ops__!{ @product::next($($rest)*) -> () }};
}
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! newtype_ops__ {
(@product::next({$($token:tt)+} $($rest:tt)*) -> $args:tt) => {
newtype_ops__!{ @product::unpack({$($token)+} $($rest)*) -> $args }};
(@product::next( $token:tt $($rest:tt)*) -> $args:tt) => {
newtype_ops__!{ @product::single($token $($rest)*) -> $args }};
(@product::next() -> $args:tt) => {
newtype_ops__!{ @interpret$args }};
(@product::single($token:tt $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @product::next($($rest)*) -> ($($args)* $token) }};
(@product::unpack({$($token:tt)*} $($rest:tt)*) -> $args:tt) => {
newtype_ops__!{ @product::unpack_2({$($token)*} [$($rest)*]) -> $args }};
(@product::unpack_2({$($token:tt)*} $rest:tt) -> $args:tt) => {
$( newtype_ops__!{ @product::unpack_3($token $rest) -> $args } )* };
(@product::unpack_3($token:tt [$($rest:tt)*]) -> $args:tt) => {
newtype_ops__!{ @product::single($token $($rest)*) -> $args }};
(@interpret($($rest:tt)*)) => { newtype_ops__!{ @interpret::type($($rest)*) -> () }};
(@interpret::type([$($T:tt)*] $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::oper($($rest)*) -> ($($args)* {value_ty:[$($T)*]}) }};
(@interpret::oper(arithmetic $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::oper(add $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(sub $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(mul $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(div $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(rem $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(neg $($rest)*) -> ($($args)*) }
};
(@interpret::oper(integer $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::oper(bitand $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(bitor $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(bitxor $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(not $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(add $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(sub $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(mul $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(div $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(rem $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(neg $($rest)*) -> ($($args)*) }
};
(@interpret::oper(arith_ring $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::oper(add $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(sub $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(mul $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(neg $($rest)*) -> ($($args)*) }
};
(@interpret::oper(bitwise $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::oper(bitand $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(bitor $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(bitxor $($rest)*) -> ($($args)*) }
newtype_ops__!{ @interpret::oper(not $($rest)*) -> ($($args)*) }
};
(@interpret::oper(add $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::Add][::std::ops::AddAssign]]}
{methods:[[add][add_assign]]}
)}};
(@interpret::oper(sub $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::Sub][::std::ops::SubAssign]]}
{methods:[[sub][sub_assign]]}
)}};
(@interpret::oper(mul $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::Mul][::std::ops::MulAssign]]}
{methods:[[mul][mul_assign]]}
)}};
(@interpret::oper(div $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::Div][::std::ops::DivAssign]]}
{methods:[[div][div_assign]]}
)}};
(@interpret::oper(rem $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::Rem][::std::ops::RemAssign]]}
{methods:[[rem][rem_assign]]}
)}};
(@interpret::oper(neg $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:unary}
{traits:[[::std::ops::Neg]]}
{methods:[[neg]]}
)}};
(@interpret::oper(bitand $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::BitAnd][::std::ops::BitAndAssign]]}
{methods:[[bitand][bitand_assign]]}
)}};
(@interpret::oper(bitor $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::BitOr][::std::ops::BitOrAssign]]}
{methods:[[bitor][bitor_assign]]}
)}};
(@interpret::oper(bitxor $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:binary}
{traits:[[::std::ops::BitXor][::std::ops::BitXorAssign]]}
{methods:[[bitxor][bitxor_assign]]}
)}};
(@interpret::oper(not $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::mode($($rest)*) -> (
$($args)* {kind:unary}
{traits:[[::std::ops::Not]]}
{methods:[[not]]}
)}};
(@interpret::mode(normal $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::self($($rest)*) -> ($($args)* {mode:normal}) }};
(@interpret::mode(assign $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::self($($rest)*) -> ($($args)* {mode:assign}) }};
(@interpret::mode(: $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::self($($rest)*) -> ($($args)* {mode:normal}) }};
(@interpret::mode(= $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::self($($rest)*) -> ($($args)* {mode:assign}) }};
(@interpret::self(&Self $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::other($($rest)*) -> ($($args)* {recv_form:[&x.0]}) }};
(@interpret::self(^Self $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::other($($rest)*) -> ($($args)* {recv_form:[x.0]}) }};
(@interpret::self(Self $($rest:tt)*) -> ($($args:tt)*)) => {
newtype_ops__!{ @interpret::other($($rest)*) -> ($($args)* {recv_form:[x.0]}) }};
(@interpret::other(&Self) -> ($($args:tt)*)) => {
newtype_ops__!{ @postprocess($($args)* {arg:[#ref]} {arg_form:[&x.0]}) }};
(@interpret::other(^Self) -> ($($args:tt)*)) => {
newtype_ops__!{ @postprocess($($args)* {arg:[#value]} {arg_form:[x.0]}) }};
(@interpret::other(Self) -> ($($args:tt)*)) => {
newtype_ops__!{ @postprocess($($args)* {arg:[#value]} {arg_form:[x.0]}) }};
(@interpret::other() -> ($($args:tt)*)) => {
newtype_ops__!{ @postprocess($($args)* {arg:[#value]} {arg_form:[x.0]}) }};
(@interpret::other(^$($rest:tt)+) -> ($($args:tt)*)) => {
newtype_ops__!{ @postprocess($($args)* {arg:[$($rest)*]} {arg_form:[x]}) }};
(@interpret::other($($rest:tt)+) -> ($($args:tt)*)) => {
newtype_ops__!{ @postprocess($($args)* {arg:[$($rest)*]} {arg_form:[x]}) }};
(@postprocess(
$value_ty:tt $kind:tt $traits:tt $methods:tt $mode:tt $recv_form:tt $arg:tt $arg_form:tt
)) => {
newtype_ops__!{ @postprocess::blacklist(
[$mode $kind $recv_form $arg_form $arg]
[$arg $value_ty $recv_form]
$traits $methods $recv_form $arg_form
) }
};
(@postprocess::blacklist([{mode:assign} {kind:unary} $($more:tt)*] $($rest:tt)*)) => {
};
(@postprocess::blacklist([{mode:assign} {kind:binary} $($more:tt)*] $($rest:tt)*)) => {
newtype_ops__!{ @postprocess::blacklist([{kind:assign} $($more)*] $($rest)*) }};
(@postprocess::blacklist([{mode:normal} {kind:unary} $($more:tt)*] $($rest:tt)*)) => {
newtype_ops__!{ @postprocess::blacklist([{kind:unary} $($more)*] $($rest)*) }};
(@postprocess::blacklist([{mode:normal} {kind:binary} $($more:tt)*] $($rest:tt)*)) => {
newtype_ops__!{ @postprocess::blacklist([{kind:binary} $($more)*] $($rest)*) }};
(@postprocess::blacklist([{kind:assign} {recv_form:[&x.0]} $arg_form:tt $arg:tt] $($rest:tt)*)) => { };
(@postprocess::blacklist([{kind:unary} $recv_form:tt {arg_form:[&x.0]} $arg:tt] $($rest:tt)*)) => { };
(@postprocess::blacklist([{kind:unary} $recv_form:tt {arg_form:[x]} $arg:tt] $($rest:tt)*)) => { };
(@postprocess::blacklist([{kind:assign} $recv_form:tt $arg_form:tt {arg:[#ref]}] $($rest:tt)*)) => { };
(@postprocess::blacklist([{kind:assign} $recv_form:tt $arg_form:tt {arg:[&$($arg:tt)+]}] $($rest:tt)*)) => { };
(@postprocess::blacklist([{kind:$kind:tt} $($dropped:tt)*] $($rest:tt)*)) => {
newtype_ops__!{ @postprocess::true_types($($rest)* {kind: $kind}) }};
(@postprocess::true_types(
[{arg:[#value]} {value_ty:[$($T:tt)*]} $recv_form:tt] $($rest:tt)*
)) => { newtype_ops__!{ @postprocess::true_types::2(
[{value_ty:[$($T)*]} $recv_form {arg:[$($T)*]}] $($rest)*
)}};
(@postprocess::true_types(
[{arg:[#ref]} {value_ty:[$($T:tt)*]} $recv_form:tt] $($rest:tt)*
)) => { newtype_ops__!{ @postprocess::true_types::2(
[{value_ty:[$($T)*]} $recv_form {arg:[&$($T)*]}] $($rest)*
)}};
(@postprocess::true_types(
[{arg:[$($arg:tt)*]} {value_ty:[$($T:tt)*]} $recv_form:tt] $($rest:tt)*
)) => { newtype_ops__!{ @postprocess::true_types::2(
[{value_ty:[$($T)*]} $recv_form {arg:[$($arg)*]}] $($rest)*
)}};
(@postprocess::true_types::2(
[{value_ty:[$($T:tt)*]} {recv_form:[&$($recv_form:tt)*]} {arg:[&$($arg:tt)*]}] $($rest:tt)*
)) => {
newtype_ops__!{ @postprocess::almost_there(
$($rest)* {tpars:[<'a,'b>]} {recv:[&'a $($T)*]} {arg:[&'b $($arg)*]} {out:[$($T)*]}
)}
};
(@postprocess::true_types::2(
[{value_ty:[$($T:tt)*]} {recv_form:[&$($recv_form:tt)*]} {arg:[$($arg:tt)*]}] $($rest:tt)*
)) => {
newtype_ops__!{ @postprocess::almost_there(
$($rest)* {tpars:[<'a>]} {recv:[&'a $($T)*]} {arg:[$($arg)*]} {out:[$($T)*]}
)}
};
(@postprocess::true_types::2(
[{value_ty:[$($T:tt)*]} {recv_form:[$($recv_form:tt)*]} {arg:[&$($arg:tt)*]}] $($rest:tt)*
)) => {
newtype_ops__!{ @postprocess::almost_there(
$($rest)* {tpars:[<'b>]} {recv:[$($T)*]} {arg:[&'b $($arg)*]} {out:[$($T)*]}
)}
};
(@postprocess::true_types::2(
[{value_ty:[$($T:tt)*]} {recv_form:[$($recv_form:tt)*]} {arg:[$($arg:tt)*]}] $($rest:tt)*
)) => {
newtype_ops__!{ @postprocess::almost_there(
$($rest)* {tpars:[]} {recv:[$($T)*]} {arg:[$($arg)*]} {out:[$($T)*]}
)}
};
(@postprocess::almost_there(
{traits:$traits:tt} {methods:$methods:tt}
{recv_form:$recv_form:tt} {arg_form:$arg_form:tt}
{kind:$kind:tt}
{tpars:$tpars:tt} {recv:$Recv:tt} {arg:$Arg:tt} {out:$Out:tt}
)) => {
newtype_ops__!{
@impl::$kind
traits:$traits methods:$methods
tpars:$tpars recv:$Recv arg:$Arg out:$Out
forms:[$recv_form $arg_form]
}
};
(@impl::unary
traits:[[$($Trait:tt)*]]
methods:[[$meth:ident]]
tpars:[$($tpars:tt)*] recv:[$Recv:ty] arg:$_Arg:tt out:[$Out:path]
forms:[[$($form1:tt)*] $_form2:tt]
) => {
impl$($tpars)* $($Trait)* for $Recv {
type Output = $Out;
fn $meth(self) -> $Out {
let this = self;
$Out(
newtype_ops__!(@helper::delegate [$($form1)*] [this] [0]).$meth()
)
}
}
};
(@impl::binary
traits:[[$($Trait:tt)*] $_TraitAssign:tt]
methods:[[$meth:ident] $_meth_assign:tt]
tpars:[$($tpars:tt)*] recv:[$Recv:ty] arg:[$Arg:ty] out:[$Out:path]
forms:[[$($form1:tt)*][$($form2:tt)*]]
) => {
impl$($tpars)* $($Trait)*<$Arg> for $Recv {
type Output = $Out;
fn $meth(self, other: $Arg) -> $Out {
let this = self;
$Out(
newtype_ops__!(@helper::delegate [$($form1)*] [this] [0])
.$meth(
newtype_ops__!(@helper::delegate [$($form2)*] [other] [0])
)
)
}
}
};
(@impl::assign
traits:[$_Trait:tt [$($TraitAssign:tt)*]]
methods:[$_meth:tt [$meth_assign:ident]]
tpars:[$($tpars:tt)*] recv:[$Recv:ty] arg:[$Arg:ty] out:$_Out:tt
forms:[$_form1:tt [$($form2:tt)*]]
) => {
impl$($tpars)* $($TraitAssign)*<$Arg> for $Recv {
fn $meth_assign(&mut self, other: $Arg) {
let this = self;
newtype_ops__!(@helper::delegate [&mut x.0] [this] [0])
.$meth_assign(
newtype_ops__!(@helper::delegate [$($form2)*] [other] [0])
)
}
}
};
(@helper::delegate [&mut x.0] [$id:ident] [$fld:tt]) => { &mut $id.$fld };
(@helper::delegate [&x.0] [$id:ident] [$fld:tt]) => { &$id.$fld };
(@helper::delegate [x.0] [$id:ident] [$fld:tt]) => { $id.$fld };
(@helper::delegate [x] [$id:ident] [$fld:tt]) => { $id };
}
#[cfg(test)]
mod tests {
mod broad_1 {
mod foo {
#[derive(Eq,PartialEq,Debug)]
pub struct Foo(pub i32);
}
#[derive(Eq,PartialEq,Debug)]
pub struct Bar(pub i32);
newtype_ops!{ {[foo::Foo][Bar]} integer {:=} {^&}Self {^&}{Self i32} }
use self::foo::Foo;
#[test]
fn test() {
assert_eq!(Foo(5), Foo(2) + Foo(3));
assert_eq!(Foo(5), Foo(2) + &Foo(3));
assert_eq!(Foo(5), &Foo(2) + Foo(3));
assert_eq!(Foo(5), &Foo(2) + &Foo(3));
assert_eq!(Foo(4), Foo(8) / 2);
assert_eq!(Foo(4), &Foo(8) / 2);
assert_eq!(Foo(4), Foo(8) / &2);
assert_eq!(Foo(4), &Foo(8) / &2);
assert_eq!(Foo(-3), -Foo(3));
assert_eq!(Foo(-3), -&Foo(3));
assert_eq!(Bar(5), Bar(2) + Bar(3));
let mut foo = Foo(4);
foo += 2;
assert_eq!(foo, Foo(6));
foo += Foo(2);
assert_eq!(foo, Foo(8));
}
}
mod proper_forwarding {
#[derive(Eq,PartialEq,Debug)]
pub struct Inner(pub i32);
#[derive(Eq,PartialEq,Debug)]
pub struct Foo(pub Inner);
newtype_ops!{ [Inner] {add div neg} {assign normal} {^&}Self {^&}{Self i32} }
newtype_ops!{ [Foo] {add div neg} {assign normal} {^&}Self {^&}{Self Inner i32} }
#[test]
fn test() {
let foo = |x| Foo(Inner(x));
assert_eq!(foo(4), foo(8) / foo(2));
assert_eq!(foo(4), foo(8) / &foo(2));
assert_eq!(foo(4), &foo(8) / foo(2));
assert_eq!(foo(4), &foo(8) / &foo(2));
assert_eq!(foo(4), foo(8) / Inner(2));
assert_eq!(foo(4), &foo(8) / Inner(2));
assert_eq!(foo(4), foo(8) / &Inner(2));
assert_eq!(foo(4), &foo(8) / &Inner(2));
assert_eq!(foo(4), foo(8) / 2);
assert_eq!(foo(4), &foo(8) / 2);
assert_eq!(foo(4), foo(8) / &2);
assert_eq!(foo(4), &foo(8) / &2);
assert_eq!(foo(-3), -foo(3));
assert_eq!(foo(-3), -&foo(3));
let mut x = foo(4);
x += foo(2);
assert_eq!(x, foo(6));
x += Inner(2);
assert_eq!(x, foo(8));
x += 2;
assert_eq!(x, foo(10));
}
}
mod op_bindings {
macro_rules! make_structs {
($($T:ident),*) => {$(
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub struct $T(pub i32);
impl From<i32> for $T {
fn from(x: i32) -> $T { $T(x) }
}
)*};
}
make_structs!{ Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Neg, Not }
newtype_ops!{ [Add] add {assign normal} Self Self }
newtype_ops!{ [Sub] sub {assign normal} Self Self }
newtype_ops!{ [Mul] mul {assign normal} Self Self }
newtype_ops!{ [Div] div {assign normal} Self Self }
newtype_ops!{ [Rem] rem {assign normal} Self Self }
newtype_ops!{ [BitAnd] bitand {assign normal} Self Self }
newtype_ops!{ [BitOr] bitor {assign normal} Self Self }
newtype_ops!{ [BitXor] bitxor {assign normal} Self Self }
newtype_ops!{ [Neg] neg {assign normal} Self }
newtype_ops!{ [Not] not {assign normal} Self }
fn run_binary_tests<T,F1,G1,F2,G2>(int_func: F1, foo_func: G1, int_eq: F2, foo_eq: G2)
where
T: ::std::fmt::Debug + From<i32> + Eq,
F1: Fn(i32, i32) -> i32, G1: Fn(T, T) -> T,
F2: Fn(&mut i32, i32), G2: Fn(&mut T, T),
{
for a in 1..10 {
for b in 1..10 {
let expected: T = int_func(a, b).into();
let actual: T = foo_func(a.into(), b.into());
assert_eq!(actual, expected, "ouchie");
let expected: T = { let mut x = a; int_eq(&mut x, b); x }.into();
let actual: T = { let mut x = a.into(); foo_eq(&mut x, b.into()); x };
assert_eq!(actual, expected, "eihcuo");
}
}
}
fn run_unary_tests<T,F,G>(int_func: F, foo_func: G)
where T: ::std::fmt::Debug + From<i32> + Eq, F: Fn(i32) -> i32, G: Fn(T) -> T
{
for a in 1..10 {
let expected: T = int_func(a).into();
let actual: T = foo_func(a.into());
assert_eq!(actual, expected, "ouchie");
}
}
#[test]
fn test() {
run_binary_tests::<Add ,_,_,_,_>(|a,b| a + b, |a,b| a + b, |a,b| *a += b, |a,b| *a += b);
run_binary_tests::<Sub ,_,_,_,_>(|a,b| a - b, |a,b| a - b, |a,b| *a -= b, |a,b| *a -= b);
run_binary_tests::<Mul ,_,_,_,_>(|a,b| a * b, |a,b| a * b, |a,b| *a *= b, |a,b| *a *= b);
run_binary_tests::<Div ,_,_,_,_>(|a,b| a / b, |a,b| a / b, |a,b| *a /= b, |a,b| *a /= b);
run_binary_tests::<Rem ,_,_,_,_>(|a,b| a % b, |a,b| a % b, |a,b| *a %= b, |a,b| *a %= b);
run_binary_tests::<BitAnd,_,_,_,_>(|a,b| a & b, |a,b| a & b, |a,b| *a &= b, |a,b| *a &= b);
run_binary_tests::<BitOr ,_,_,_,_>(|a,b| a | b, |a,b| a | b, |a,b| *a |= b, |a,b| *a |= b);
run_binary_tests::<BitXor,_,_,_,_>(|a,b| a ^ b, |a,b| a ^ b, |a,b| *a ^= b, |a,b| *a ^= b);
run_unary_tests::<Neg,_,_>(|a| -a, |a| -a);
run_unary_tests::<Not,_,_>(|a| !a, |a| !a);
}
#[test] #[should_panic(expected = "ouchie")]
fn bad_binary() {
run_binary_tests::<Add ,_,_,_,_>(|a,b| a * b, |a,b| a + b, |a,b| *a += b, |a,b| *a += b);
}
#[test] #[should_panic(expected = "eihcuo")]
fn bad_assign() {
run_binary_tests::<Add ,_,_,_,_>(|a,b| a + b, |a,b| a + b, |a,b| *a *= b, |a,b| *a += b);
}
#[test] #[should_panic(expected = "ouchie")]
fn bad_unary() {
run_unary_tests::<Neg,_,_>(|a| !a, |a| -a);
}
}
mod string {
#[derive(PartialEq,Clone,Debug)]
pub struct MyString(String);
newtype_ops!{ [MyString] {add} {:=} ^Self &{Self str} }
#[test]
fn test() {
assert_eq!(
MyString("Hello world".to_string()) + "!",
MyString("Hello world!".to_string())
)
}
#[cfg(nope)]
#[test]
fn victim() {
let mut s = MyString("Hello world".to_string());
s += "!";
assert_eq!(s, MyString("Hello world!".to_string()));
}
}
}