safe_arithmetic/ops/
checked_div.rs

1use crate::error::{DivideByZero, Overflow, Underflow};
2use num::Zero;
3use std::fmt::{self, Debug, Display};
4
5pub trait CheckedDiv<Rhs = Self>
6where
7    Self: Sized,
8{
9    type Output;
10    type Error;
11
12    /// Checked arithmetic division of self
13    ///
14    /// # Errors
15    /// When the result of the division can not be represented (e.g. due to an overflow).
16    fn checked_div(self, scalar: Rhs) -> Result<Self::Output, Self::Error>;
17}
18
19macro_rules! impl_unsigned_checked_div {
20    ( $T:ty ) => {
21        impl CheckedDiv for $T {
22            type Output = Self;
23            type Error = DivError<Self, Self>;
24
25            fn checked_div(self, rhs: Self) -> Result<Self::Output, Self::Error> {
26                // can fail if rhs == 0
27                if rhs.is_zero() {
28                    Err(DivError(self.divide_by_zero()))
29                } else {
30                    num::CheckedDiv::checked_div(&self, &rhs)
31                        .ok_or(rhs.underflows(self))
32                        .map_err(DivError)
33                }
34            }
35        }
36    };
37}
38
39impl_unsigned_checked_div!(u32);
40
41macro_rules! impl_signed_checked_div {
42    ( $T:ty ) => {
43        impl CheckedDiv for $T {
44            type Output = Self;
45            type Error = DivError<Self, Self>;
46
47            fn checked_div(self, rhs: Self) -> Result<Self::Output, Self::Error> {
48                // can fail if rhs == 0
49                if rhs.is_zero() {
50                    Err(DivError(self.divide_by_zero()))
51                } else if self.signum() == rhs.signum() {
52                    // can also overflow
53                    num::CheckedDiv::checked_div(&self, &rhs)
54                        .ok_or(rhs.overflows(self))
55                        .map_err(DivError)
56                } else {
57                    // can also underflow?
58                    num::CheckedDiv::checked_div(&self, &rhs)
59                        .ok_or(rhs.underflows(self))
60                        .map_err(DivError)
61                }
62            }
63        }
64    };
65}
66
67impl_signed_checked_div!(i64);
68
69macro_rules! impl_float_checked_div {
70    ( $T:ty ) => {
71        impl CheckedDiv for $T {
72            type Output = Self;
73            type Error = DivError<Self, Self>;
74
75            fn checked_div(self, rhs: Self) -> Result<Self::Output, Self::Error> {
76                // can fail if rhs == 0
77                if rhs.is_zero() {
78                    return Err(DivError(self.divide_by_zero()));
79                }
80                let result = self / rhs;
81                if result.is_nan() && self.signum() == rhs.signum() {
82                    // can also overflow
83                    Err(DivError(rhs.overflows(self)))
84                } else if result.is_nan() {
85                    // can also underflow?
86                    Err(DivError(rhs.underflows(self)))
87                } else {
88                    Ok(result)
89                }
90            }
91        }
92    };
93}
94
95impl_float_checked_div!(f64);
96
97#[derive(PartialEq, Clone, Debug)]
98#[allow(clippy::module_name_repetitions)]
99pub struct DivError<Lhs, Rhs>(pub crate::error::Operation<Lhs, Rhs>);
100
101impl<Lhs, Rhs> crate::error::Arithmetic for DivError<Lhs, Rhs>
102where
103    Lhs: crate::Type,
104    Rhs: crate::Type,
105{
106}
107
108impl<Lhs, Rhs> std::error::Error for DivError<Lhs, Rhs>
109where
110    Lhs: Display + Debug,
111    Rhs: Display + Debug,
112{
113    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
114        self.0.cause.as_deref().map(crate::error::AsErr::as_err)
115    }
116}
117
118impl<Lhs, Rhs> Display for DivError<Lhs, Rhs>
119where
120    Lhs: Display,
121    Rhs: Display,
122{
123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124        match self.0.kind {
125            Some(kind) => match kind {
126                crate::error::Kind::DivideByZero => {
127                    write!(f, "dividing {} by {} is undefined", self.0.lhs, self.0.rhs)
128                }
129                other => {
130                    write!(
131                        f,
132                        "dividing {} by {} would {} {}",
133                        self.0.lhs,
134                        self.0.rhs,
135                        other,
136                        std::any::type_name::<Lhs>(),
137                    )
138                }
139            },
140            None => write!(f, "cannot divide {} by {}", self.0.lhs, self.0.rhs),
141        }
142    }
143}