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<Rhs, Output>`: operators with an arbitrary operand and output.
- `NumAssignOps<Rhs>`: 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
This commit is contained in:
Josh Stone 2017-04-23 15:15:44 -07:00
parent ef08fe2f96
commit 21b520ea15
1 changed files with 122 additions and 4 deletions

View File

@ -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<Output = Self> + Sub<Output = Self>
+ Mul<Output = Self> + Div<Output = Self> + Rem<Output = Self>
/// 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<Self, Self::FromStrRadixErr>;
}
/// The trait for types implementing basic numeric operations
///
/// This is automatically implemented for types which implement the operators.
pub trait NumOps<Rhs = Self, Output = Self>
: Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{}
impl<T, Rhs, Output> NumOps<Rhs, Output> for T
where T: Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{}
/// 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<T> 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<Base>: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
impl<'a, T> RefNum<T> for &'a T where &'a T: NumOps<T, T> + 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<Rhs = Self>
: AddAssign<Rhs>
+ SubAssign<Rhs>
+ MulAssign<Rhs>
+ DivAssign<Rhs>
+ RemAssign<Rhs>
{}
impl<T, Rhs> NumAssignOps<Rhs> for T
where T: AddAssign<Rhs>
+ SubAssign<Rhs>
+ MulAssign<Rhs>
+ DivAssign<Rhs>
+ RemAssign<Rhs>
{}
/// 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<T> 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<T> 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<T: Num + Copy>(x: T, y: T) -> T {
x * y / y % y + y - y
}
assert_eq!(compute(1, 2), 1)
}
#[test]
fn check_numref_ops() {
fn compute<T: NumRef>(x: T, y: &T) -> T {
x * y / y % y + y - y
}
assert_eq!(compute(1, &2), 1)
}
#[test]
fn check_refnum_ops() {
fn compute<T: Copy>(x: &T, y: T) -> T
where for<'a> &'a T: RefNum<T>
{
&(&(&(&(x * y) / y) % y) + y) - y
}
assert_eq!(compute(&1, 2), 1)
}
#[test]
fn check_refref_ops() {
fn compute<T>(x: &T, y: &T) -> T
where for<'a> &'a T: RefNum<T>
{
&(&(&(&(x * y) / y) % y) + y) - y
}
assert_eq!(compute(&1, &2), 1)
}
#[test]
fn check_numassign_ops() {
fn compute<T: NumAssign + Copy>(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)