From 21b520ea15a07b2197c4c6950a4996a182b56619 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sun, 23 Apr 2017 15:15:44 -0700 Subject: [PATCH] Add new traits for reference and assignment operators There are two new "utility" traits covering the basic operators: `Add`, `Sub`, `Mul`, `Div`, and `Rem`. - `NumOps`: operators with an arbitrary operand and output. - `NumAssignOps`: assignment operators with an arbitrary operand. Then the new collection of numeric traits are: - `Num`: effectively unchanged, just taking operands by value. - `NumRef`: `Num` adding reference operands on the right side. - `RefNum`: `&T` operators, with either `T` or `&T` on the right side. - This does not specify `T: Num`, as rust-lang/rust#20671 means that could only add a constraint, without implying its presence for use. - `NumAssign`: `Num` adding assignment operators by value. - `NumAssignRef`: `NumAssign` adding reference assignment operators. - Nothing actually implements this yet! Acknowledgement: this is roughly based on [@andersk's suggestion][1]. [1] https://github.com/rust-num/num/issues/94#issuecomment-269073071 --- traits/src/lib.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 4 deletions(-) diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 083fe41..f4dbee2 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -15,6 +15,7 @@ html_playground_url = "http://play.integer32.com/")] use std::ops::{Add, Sub, Mul, Div, Rem}; +use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; use std::num::Wrapping; pub use bounds::Bounded; @@ -37,10 +38,9 @@ pub mod cast; pub mod int; pub mod pow; -/// The base trait for numeric types -pub trait Num: PartialEq + Zero + One - + Add + Sub - + Mul + Div + Rem +/// The base trait for numeric types, covering `0` and `1` values, +/// comparisons, basic numeric operations, and string conversion. +pub trait Num: PartialEq + Zero + One + NumOps { type FromStrRadixErr; @@ -60,6 +60,72 @@ pub trait Num: PartialEq + Zero + One fn from_str_radix(str: &str, radix: u32) -> Result; } +/// The trait for types implementing basic numeric operations +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumOps + : Add + + Sub + + Mul + + Div + + Rem +{} + +impl NumOps for T +where T: Add + + Sub + + Mul + + Div + + Rem +{} + +/// The trait for `Num` types which also implement numeric operations taking +/// the second operand by reference. +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumRef: Num + for<'r> NumOps<&'r Self> {} +impl NumRef for T where T: Num + for<'r> NumOps<&'r T> {} + +/// The trait for references which implement numeric operations, taking the +/// second operand either by value or by reference. +/// +/// This is automatically implemented for all `&T` implementing the operators. +pub trait RefNum: NumOps + for<'r> NumOps<&'r Base, Base> {} +impl<'a, T> RefNum for &'a T where &'a T: NumOps + for<'r> NumOps<&'r T, T> {} + +/// The trait for types implementing numeric assignment operators (like `+=`). +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumAssignOps + : AddAssign + + SubAssign + + MulAssign + + DivAssign + + RemAssign +{} + +impl NumAssignOps for T +where T: AddAssign + + SubAssign + + MulAssign + + DivAssign + + RemAssign +{} + +/// The trait for `Num` types which also implement assignment operators. +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumAssign: Num + NumAssignOps {} +impl NumAssign for T where T: Num + NumAssignOps {} + +/// The trait for `NumAssign` types which also implement assignment operations +/// taking the second operand by reference. +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {} +impl NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {} + + macro_rules! int_trait_impl { ($name:ident for $($t:ty)*) => ($( impl $name for $t { @@ -315,3 +381,55 @@ fn wrapping_is_num() { fn wrapping_from_str_radix() { test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); } + +#[test] +fn check_num_ops() { + fn compute(x: T, y: T) -> T { + x * y / y % y + y - y + } + assert_eq!(compute(1, 2), 1) +} + +#[test] +fn check_numref_ops() { + fn compute(x: T, y: &T) -> T { + x * y / y % y + y - y + } + assert_eq!(compute(1, &2), 1) +} + +#[test] +fn check_refnum_ops() { + fn compute(x: &T, y: T) -> T + where for<'a> &'a T: RefNum + { + &(&(&(&(x * y) / y) % y) + y) - y + } + assert_eq!(compute(&1, 2), 1) +} + +#[test] +fn check_refref_ops() { + fn compute(x: &T, y: &T) -> T + where for<'a> &'a T: RefNum + { + &(&(&(&(x * y) / y) % y) + y) - y + } + assert_eq!(compute(&1, &2), 1) +} + +#[test] +fn check_numassign_ops() { + fn compute(mut x: T, y: T) -> T { + x *= y; + x /= y; + x %= y; + x += y; + x -= y; + x + } + assert_eq!(compute(1, 2), 1) +} + +// TODO test `NumAssignRef`, but even the standard numeric types don't +// implement this yet. (see rust pr41336)