//! Provides a macro make checked math pretty easy. //! //! ``` //! # #[macro_use] extern crate cheque; //! # fn main() { //! let a = 10u8; //! let b = 20u8; //! //! let_checked![a, b]; //! //! assert_eq!(a + b, 30); //! assert_eq!(b * b, None); //! assert_eq!(b / 0, None); //! assert_eq!(a - 20, None); //! assert_eq!((a - b) + 1, None); //! # } //! ``` //! //! `let_checked!` redeclares each identifier as a checked numeric value. //! You can then use `+`, etc. on the checked variables, and then deref the result to get an //! `Option<_>`. //! //! You can also use numeric literals/unchecked values, so long as they are on the right side of //! the operation. //! //! ``` //! # #[macro_use] extern crate cheque; //! # fn main() { //! let c = 20usize; //! let_checked![c]; //! //! if let Some(scary) = *(c - 100) { //! panic!("Ahh! {:?}", scary); //! } //! # } //! ``` //! //! //! //! If you are doing generic programming, you should add the [checked num_traits] to your //! `where` bounds. //! [checked num_traits]: http://rust-num.github.io/num/num_traits/ops/checked/index.html extern crate num_traits; use std::ops::*; use std::cmp::PartialEq; use num_traits::ops::checked::*; #[macro_export] macro_rules! let_checked { ($($ident:ident),*) => {$( let $ident = $crate::Checker(Some($ident)); )*}; ($($ident:ident,)*) => {$( let $ident = $crate::Checker(Some($ident)); )*}; } #[derive(Debug, Copy, Clone, PartialEq, Eq)] /// This implements `Deref>`. pub struct Checker( #[doc(hidden)] pub /*(macro)*/ Option); impl Deref for Checker { type Target = Option; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Checker { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl PartialEq for Checker where T: PartialEq + Copy { fn eq(&self, other: &T) -> bool { self.0 == Some(*other) } } impl PartialEq> for Checker where T: PartialEq { fn eq(&self, other: &Option) -> bool { &self.0 == other } } macro_rules! impl_checked { ($Vanilla:ident, $vanilla_fn:ident, $Checked:ident, $checked_fn:ident) => { impl $Vanilla for Checker where T: $Checked { type Output = Self; #[inline] fn $vanilla_fn(self, rhs: Checker) -> Self { if let (Some(l), Some(r)) = (self.0, rhs.0) { Checker(l.$checked_fn(&r)) } else { Checker(None) } } } impl $Vanilla for Checker where T: $Checked { type Output = Self; #[inline] fn $vanilla_fn(self, rhs: T) -> Self { Checker(self.0.and_then(|l| l.$checked_fn(&rhs))) } } } } impl_checked![Add, add, CheckedAdd, checked_add]; impl_checked![Sub, sub, CheckedSub, checked_sub]; impl_checked![Mul, mul, CheckedMul, checked_mul]; impl_checked![Div, div, CheckedDiv, checked_div]; #[cfg(test)] mod test { #[test] fn compiles() { let a = 10; let_checked![a]; let _ = a + a; let _ = a - a; let _ = a * a; let _ = a / a; let _ = a + 1; let _ = a - 1; let _ = a * 1; let _ = a / 1; } #[test] fn divide_by_zero() { let b = 1u8; let z = 0u8; let_checked![b, z, /* assert compilation w/ comma */ ]; assert_eq!(b / z, None); } #[test] fn empty_invoke() { let_checked![]; //... how does it disambiguate? o_O } }