Skip to content

Commit 52e86e2

Browse files
committed
Derive NumOps<Self, Self> for newtypes
This equates to deriving Add, Sub, Mul, Div, and Rem, where RHS=Self and Output=Self.
1 parent 75b8972 commit 52e86e2

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

src/lib.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,52 @@ pub fn to_primitive(input: TokenStream) -> TokenStream {
447447

448448
dummy_const_trick("ToPrimitive", &name, impl_).into()
449449
}
450+
451+
const NEWTYPE_ONLY: &'static str = "This trait can only be derived for newtypes";
452+
453+
/// Derives [`num_traits::NumOps`][num_ops] for newtypes. The inner type must already implement
454+
/// `NumOps`.
455+
///
456+
/// [num_ops]: https://docs.rs/num-traits/0.2/num_traits/trait.NumOps.html
457+
///
458+
/// Note that, since `NumOps` is really a trait alias for `Add + Sub + Mul + Div + Rem`, this macro
459+
/// generates impls for _those_ traits. Furthermore, in all generated impls, `RHS=Self` and
460+
/// `Output=Self`.
461+
#[proc_macro_derive(NumOps)]
462+
pub fn num_ops(input: TokenStream) -> TokenStream {
463+
let ast: syn::DeriveInput = syn::parse(input).unwrap();
464+
let name = &ast.ident;
465+
let inner_ty = newtype_inner(&ast.data).expect(NEWTYPE_ONLY);
466+
dummy_const_trick("NumOps", &name, quote! {
467+
impl ::std::ops::Add for #name {
468+
type Output = Self;
469+
fn add(self, other: Self) -> Self {
470+
#name(<#inner_ty as ::std::ops::Add>::add(self.0, other.0))
471+
}
472+
}
473+
impl ::std::ops::Sub for #name {
474+
type Output = Self;
475+
fn sub(self, other: Self) -> Self {
476+
#name(<#inner_ty as ::std::ops::Sub>::sub(self.0, other.0))
477+
}
478+
}
479+
impl ::std::ops::Mul for #name {
480+
type Output = Self;
481+
fn mul(self, other: Self) -> Self {
482+
#name(<#inner_ty as ::std::ops::Mul>::mul(self.0, other.0))
483+
}
484+
}
485+
impl ::std::ops::Div for #name {
486+
type Output = Self;
487+
fn div(self, other: Self) -> Self {
488+
#name(<#inner_ty as ::std::ops::Div>::div(self.0, other.0))
489+
}
490+
}
491+
impl ::std::ops::Rem for #name {
492+
type Output = Self;
493+
fn rem(self, other: Self) -> Self {
494+
#name(<#inner_ty as ::std::ops::Rem>::rem(self.0, other.0))
495+
}
496+
}
497+
}).into()
498+
}

tests/newtype.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use num_renamed::{FromPrimitive, ToPrimitive};
1212
PartialOrd,
1313
ToPrimitive,
1414
FromPrimitive,
15+
NumOps,
1516
)]
1617
struct MyFloat(f64);
1718

@@ -24,3 +25,12 @@ fn test_from_primitive() {
2425
fn test_to_primitive() {
2526
assert_eq!(MyFloat(25.0).to_u32(), Some(25));
2627
}
28+
29+
#[test]
30+
fn test_num_ops() {
31+
assert_eq!(MyFloat(25.0) + MyFloat(10.0), MyFloat(35.0));
32+
assert_eq!(MyFloat(25.0) - MyFloat(10.0), MyFloat(15.0));
33+
assert_eq!(MyFloat(25.0) * MyFloat(2.0), MyFloat(50.0));
34+
assert_eq!(MyFloat(25.0) / MyFloat(10.0), MyFloat(2.5));
35+
assert_eq!(MyFloat(25.0) % MyFloat(10.0), MyFloat(5.0));
36+
}

0 commit comments

Comments
 (0)