Remove Copy from Ensure* traits (#13043)

* Remove Copy from EnsureOp and EnsureOpAssign

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove Copy from EnsureFrom and EnsureInto

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix default impl

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Reuse assignment code in Ensure trait

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Require Ensure for all BaseArithmetic types

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix assign impls

Co-authored-by: Luis Enrique Muñoz Martín <lemunozm@gmail.com>

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add success doc tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Oliver Tale-Yazdi
2023-01-04 14:00:30 +01:00
committed by GitHub
parent 841fcaba2c
commit 78621b8dac
+251 -53
View File
@@ -57,6 +57,7 @@ pub trait BaseArithmetic:
+ CheckedMul
+ CheckedDiv
+ CheckedRem
+ Ensure
+ Saturating
+ PartialOrd<Self>
+ Ord
@@ -113,6 +114,7 @@ impl<
+ CheckedMul
+ CheckedDiv
+ CheckedRem
+ Ensure
+ Saturating
+ PartialOrd<Self>
+ Ord
@@ -344,13 +346,24 @@ mod ensure {
use crate::{ArithmeticError, FixedPointNumber, FixedPointOperand};
/// Performs addition that returns [`ArithmeticError`] instead of wrapping around on overflow.
pub trait EnsureAdd: CheckedAdd + PartialOrd + Zero + Copy {
pub trait EnsureAdd: EnsureAddAssign {
/// Adds two numbers, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// Similar to [`CheckedAdd::checked_add()`] but returning an [`ArithmeticError`] error.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureAdd;
///
/// let a: i32 = 10;
/// let b: i32 = 20;
///
/// assert_eq!(a.ensure_add(b), Ok(30));
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureAdd, ArithmeticError};
///
@@ -367,20 +380,32 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_add(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_add(&v).ok_or_else(|| error::equivalent(v))
fn ensure_add(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_add_assign(v)?;
Ok(self)
}
}
/// Performs subtraction that returns [`ArithmeticError`] instead of wrapping around on
/// underflow.
pub trait EnsureSub: CheckedSub + PartialOrd + Zero + Copy {
pub trait EnsureSub: EnsureSubAssign {
/// Subtracts two numbers, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// Similar to [`CheckedSub::checked_sub()`] but returning an [`ArithmeticError`] error.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureSub;
///
/// let a: i32 = 10;
/// let b: i32 = 20;
///
/// assert_eq!(a.ensure_sub(b), Ok(-10));
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureSub, ArithmeticError};
///
@@ -397,20 +422,32 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_sub(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_sub(&v).ok_or_else(|| error::inverse(v))
fn ensure_sub(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_sub_assign(v)?;
Ok(self)
}
}
/// Performs multiplication that returns [`ArithmeticError`] instead of wrapping around on
/// overflow.
pub trait EnsureMul: CheckedMul + PartialOrd + Zero + Copy {
pub trait EnsureMul: EnsureMulAssign {
/// Multiplies two numbers, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// Similar to [`CheckedMul::checked_mul()`] but returning an [`ArithmeticError`] error.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureMul;
///
/// let a: i32 = 10;
/// let b: i32 = 20;
///
/// assert_eq!(a.ensure_mul(b), Ok(200));
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureMul, ArithmeticError};
///
@@ -427,19 +464,31 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_mul(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_mul(&v).ok_or_else(|| error::multiplication(self, v))
fn ensure_mul(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_mul_assign(v)?;
Ok(self)
}
}
/// Performs division that returns [`ArithmeticError`] instead of wrapping around on overflow.
pub trait EnsureDiv: CheckedDiv + PartialOrd + Zero + Copy {
pub trait EnsureDiv: EnsureDivAssign {
/// Divides two numbers, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// Similar to [`CheckedDiv::checked_div()`] but returning an [`ArithmeticError`] error.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureDiv;
///
/// let a: i32 = 20;
/// let b: i32 = 10;
///
/// assert_eq!(a.ensure_div(b), Ok(2));
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureDiv, ArithmeticError};
///
@@ -456,15 +505,16 @@ mod ensure {
/// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero));
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_div(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_div(&v).ok_or_else(|| error::division(self, v))
fn ensure_div(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_div_assign(v)?;
Ok(self)
}
}
impl<T: CheckedAdd + PartialOrd + Zero + Copy> EnsureAdd for T {}
impl<T: CheckedSub + PartialOrd + Zero + Copy> EnsureSub for T {}
impl<T: CheckedMul + PartialOrd + Zero + Copy> EnsureMul for T {}
impl<T: CheckedDiv + PartialOrd + Zero + Copy> EnsureDiv for T {}
impl<T: EnsureAddAssign> EnsureAdd for T {}
impl<T: EnsureSubAssign> EnsureSub for T {}
impl<T: EnsureMulAssign> EnsureMul for T {}
impl<T: EnsureDivAssign> EnsureDiv for T {}
/// Meta trait that supports all immutable arithmetic `Ensure*` operations
pub trait EnsureOp: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv {}
@@ -472,11 +522,23 @@ mod ensure {
/// Performs self addition that returns [`ArithmeticError`] instead of wrapping around on
/// overflow.
pub trait EnsureAddAssign: EnsureAdd {
pub trait EnsureAddAssign: CheckedAdd + PartialOrd + Zero {
/// Adds two numbers overwriting the left hand one, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureAddAssign;
///
/// let mut a: i32 = 10;
/// let b: i32 = 20;
///
/// a.ensure_add_assign(b).unwrap();
/// assert_eq!(a, 30);
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureAddAssign, ArithmeticError};
///
@@ -496,18 +558,30 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_add_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.ensure_add(v)?;
*self = self.checked_add(&v).ok_or_else(|| error::equivalent(&v))?;
Ok(())
}
}
/// Performs self subtraction that returns [`ArithmeticError`] instead of wrapping around on
/// underflow.
pub trait EnsureSubAssign: EnsureSub {
pub trait EnsureSubAssign: CheckedSub + PartialOrd + Zero {
/// Subtracts two numbers overwriting the left hand one, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureSubAssign;
///
/// let mut a: i32 = 10;
/// let b: i32 = 20;
///
/// a.ensure_sub_assign(b).unwrap();
/// assert_eq!(a, -10);
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureSubAssign, ArithmeticError};
///
@@ -527,18 +601,30 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_sub_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.ensure_sub(v)?;
*self = self.checked_sub(&v).ok_or_else(|| error::inverse(&v))?;
Ok(())
}
}
/// Performs self multiplication that returns [`ArithmeticError`] instead of wrapping around on
/// overflow.
pub trait EnsureMulAssign: EnsureMul {
pub trait EnsureMulAssign: CheckedMul + PartialOrd + Zero {
/// Multiplies two numbers overwriting the left hand one, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureMulAssign;
///
/// let mut a: i32 = 10;
/// let b: i32 = 20;
///
/// a.ensure_mul_assign(b).unwrap();
/// assert_eq!(a, 200);
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureMulAssign, ArithmeticError};
///
@@ -558,18 +644,30 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_mul_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.ensure_mul(v)?;
*self = self.checked_mul(&v).ok_or_else(|| error::multiplication(self, &v))?;
Ok(())
}
}
/// Performs self division that returns [`ArithmeticError`] instead of wrapping around on
/// overflow.
pub trait EnsureDivAssign: EnsureDiv {
pub trait EnsureDivAssign: CheckedDiv + PartialOrd + Zero {
/// Divides two numbers overwriting the left hand one, checking for overflow.
///
/// If it fails, [`ArithmeticError`] is returned.
///
/// # Examples
///
/// ```
/// use sp_arithmetic::traits::EnsureDivAssign;
///
/// let mut a: i32 = 20;
/// let b: i32 = 10;
///
/// a.ensure_div_assign(b).unwrap();
/// assert_eq!(a, 2);
/// ```
///
/// ```
/// use sp_arithmetic::{traits::EnsureDivAssign, ArithmeticError, FixedI64};
///
@@ -589,15 +687,15 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_div_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.ensure_div(v)?;
*self = self.checked_div(&v).ok_or_else(|| error::division(self, &v))?;
Ok(())
}
}
impl<T: EnsureAdd> EnsureAddAssign for T {}
impl<T: EnsureSub> EnsureSubAssign for T {}
impl<T: EnsureMul> EnsureMulAssign for T {}
impl<T: EnsureDiv> EnsureDivAssign for T {}
impl<T: CheckedAdd + PartialOrd + Zero> EnsureAddAssign for T {}
impl<T: CheckedSub + PartialOrd + Zero> EnsureSubAssign for T {}
impl<T: CheckedMul + PartialOrd + Zero> EnsureMulAssign for T {}
impl<T: CheckedDiv + PartialOrd + Zero> EnsureDivAssign for T {}
/// Meta trait that supports all assigned arithmetic `Ensure*` operations
pub trait EnsureOpAssign:
@@ -609,7 +707,6 @@ mod ensure {
{
}
/// Meta trait that supports all arithmetic operations
pub trait Ensure: EnsureOp + EnsureOpAssign {}
impl<T: EnsureOp + EnsureOpAssign> Ensure for T {}
@@ -643,7 +740,7 @@ mod ensure {
d: D,
) -> Result<Self, ArithmeticError> {
<Self as FixedPointNumber>::checked_from_rational(n, d)
.ok_or_else(|| error::division(n, d))
.ok_or_else(|| error::division(&n, &d))
}
/// Ensure multiplication for integer type `N`. Equal to `self * n`.
@@ -670,7 +767,7 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_mul_int<N: FixedPointOperand>(self, n: N) -> Result<N, ArithmeticError> {
self.checked_mul_int(n).ok_or_else(|| error::multiplication(self, n))
self.checked_mul_int(n).ok_or_else(|| error::multiplication(&self, &n))
}
/// Ensure division for integer type `N`. Equal to `self / d`.
@@ -697,16 +794,14 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_div_int<D: FixedPointOperand>(self, d: D) -> Result<D, ArithmeticError> {
self.checked_div_int(d).ok_or_else(|| error::division(self, d))
self.checked_div_int(d).ok_or_else(|| error::division(&self, &d))
}
}
impl<T: FixedPointNumber> EnsureFixedPointNumber for T {}
/// Similar to [`TryFrom`] but returning an [`ArithmeticError`] error.
pub trait EnsureFrom<T: PartialOrd + Zero + Copy>:
TryFrom<T> + PartialOrd + Zero + Copy
{
pub trait EnsureFrom<T: PartialOrd + Zero>: TryFrom<T> + PartialOrd + Zero {
/// Performs the conversion returning an [`ArithmeticError`] if fails.
///
/// Similar to [`TryFrom::try_from()`] but returning an [`ArithmeticError`] error.
@@ -728,14 +823,13 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_from(other: T) -> Result<Self, ArithmeticError> {
Self::try_from(other).map_err(|_| error::equivalent(other))
let err = error::equivalent(&other);
Self::try_from(other).map_err(|_| err)
}
}
/// Similar to [`TryInto`] but returning an [`ArithmeticError`] error.
pub trait EnsureInto<T: PartialOrd + Zero + Copy>:
TryInto<T> + PartialOrd + Zero + Copy
{
pub trait EnsureInto<T: PartialOrd + Zero>: TryInto<T> + PartialOrd + Zero {
/// Performs the conversion returning an [`ArithmeticError`] if fails.
///
/// Similar to [`TryInto::try_into()`] but returning an [`ArithmeticError`] error
@@ -757,12 +851,13 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_into(self) -> Result<T, ArithmeticError> {
self.try_into().map_err(|_| error::equivalent(self))
let err = error::equivalent(&self);
self.try_into().map_err(|_| err)
}
}
impl<T: TryFrom<S> + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureFrom<S> for T {}
impl<T: TryInto<S> + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureInto<S> for T {}
impl<T: TryFrom<S> + PartialOrd + Zero, S: PartialOrd + Zero> EnsureFrom<S> for T {}
impl<T: TryInto<S> + PartialOrd + Zero, S: PartialOrd + Zero> EnsureInto<S> for T {}
mod error {
use super::{ArithmeticError, Zero};
@@ -773,9 +868,9 @@ mod ensure {
Positive,
}
impl<T: PartialOrd + Zero> From<T> for Signum {
fn from(value: T) -> Self {
if value < Zero::zero() {
impl<T: PartialOrd + Zero> From<&T> for Signum {
fn from(value: &T) -> Self {
if value < &Zero::zero() {
Signum::Negative
} else {
Signum::Positive
@@ -795,23 +890,23 @@ mod ensure {
}
}
pub fn equivalent<R: PartialOrd + Zero + Copy>(r: R) -> ArithmeticError {
pub fn equivalent<R: PartialOrd + Zero>(r: &R) -> ArithmeticError {
match Signum::from(r) {
Signum::Negative => ArithmeticError::Underflow,
Signum::Positive => ArithmeticError::Overflow,
}
}
pub fn inverse<R: PartialOrd + Zero + Copy>(r: R) -> ArithmeticError {
pub fn inverse<R: PartialOrd + Zero>(r: &R) -> ArithmeticError {
match Signum::from(r) {
Signum::Negative => ArithmeticError::Overflow,
Signum::Positive => ArithmeticError::Underflow,
}
}
pub fn multiplication<L: PartialOrd + Zero + Copy, R: PartialOrd + Zero + Copy>(
l: L,
r: R,
pub fn multiplication<L: PartialOrd + Zero, R: PartialOrd + Zero>(
l: &L,
r: &R,
) -> ArithmeticError {
match Signum::from(l) * Signum::from(r) {
Signum::Negative => ArithmeticError::Underflow,
@@ -819,9 +914,9 @@ mod ensure {
}
}
pub fn division<N: PartialOrd + Zero + Copy, D: PartialOrd + Zero + Copy>(
n: N,
d: D,
pub fn division<N: PartialOrd + Zero, D: PartialOrd + Zero>(
n: &N,
d: &D,
) -> ArithmeticError {
if d.is_zero() {
ArithmeticError::DivisionByZero
@@ -831,3 +926,106 @@ mod ensure {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ArithmeticError;
use rand::{seq::SliceRandom, thread_rng, Rng};
#[test]
fn ensure_add_works() {
test_ensure(values(), &EnsureAdd::ensure_add, &CheckedAdd::checked_add);
}
#[test]
fn ensure_sub_works() {
test_ensure(values(), &EnsureSub::ensure_sub, &CheckedSub::checked_sub);
}
#[test]
fn ensure_mul_works() {
test_ensure(values(), &EnsureMul::ensure_mul, &CheckedMul::checked_mul);
}
#[test]
fn ensure_div_works() {
test_ensure(values(), &EnsureDiv::ensure_div, &CheckedDiv::checked_div);
}
#[test]
fn ensure_add_assign_works() {
test_ensure_assign(values(), &EnsureAddAssign::ensure_add_assign, &EnsureAdd::ensure_add);
}
#[test]
fn ensure_sub_assign_works() {
test_ensure_assign(values(), &EnsureSubAssign::ensure_sub_assign, &EnsureSub::ensure_sub);
}
#[test]
fn ensure_mul_assign_works() {
test_ensure_assign(values(), &EnsureMulAssign::ensure_mul_assign, &&EnsureMul::ensure_mul);
}
#[test]
fn ensure_div_assign_works() {
test_ensure_assign(values(), &EnsureDivAssign::ensure_div_assign, &EnsureDiv::ensure_div);
}
/// Test that the ensured function returns the expected un-ensured value.
fn test_ensure<V, E, P>(pairs: Vec<(V, V)>, ensured: E, unensured: P)
where
V: Ensure + core::fmt::Debug + Copy,
E: Fn(V, V) -> Result<V, ArithmeticError>,
P: Fn(&V, &V) -> Option<V>,
{
for (a, b) in pairs.into_iter() {
match ensured(a, b) {
Ok(c) => {
assert_eq!(unensured(&a, &b), Some(c))
},
Err(_) => {
assert!(unensured(&a, &b).is_none());
},
}
}
}
/// Test that the ensured function modifies `self` to the expected un-ensured value.
fn test_ensure_assign<V, E, P>(pairs: Vec<(V, V)>, ensured: E, unensured: P)
where
V: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy,
E: Fn(&mut V, V) -> Result<(), ArithmeticError>,
P: Fn(V, V) -> Result<V, ArithmeticError> + std::panic::RefUnwindSafe,
{
for (mut a, b) in pairs.into_iter() {
let old_a = a;
match ensured(&mut a, b) {
Ok(()) => {
assert_eq!(unensured(old_a, b), Ok(a));
},
Err(err) => {
assert_eq!(a, old_a, "A stays unmodified in the error case");
assert_eq!(unensured(old_a, b), Err(err));
},
}
}
}
/// Generates some good values for testing integer arithmetic.
fn values() -> Vec<(i32, i32)> {
let mut rng = thread_rng();
let mut one_dimension = || {
let mut ret = vec![0i32; 1007];
// Some hard-coded interesting values.
ret[..7].copy_from_slice(&[-1, 0, 1, i32::MIN, i32::MAX, i32::MAX - 1, i32::MIN + 1]);
// … and some random ones.
rng.fill(&mut ret[7..]);
ret.shuffle(&mut rng);
ret
};
one_dimension().into_iter().zip(one_dimension().into_iter()).collect()
}
}