diff --git a/src/cast.rs b/src/cast.rs index 630341f..5f45bd5 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -238,35 +238,93 @@ macro_rules! impl_to_primitive_float_to_float { ) } +macro_rules! impl_to_primitive_float_to_signed_int { + ($SrcT:ident, $DstT:ident, $slf:expr) => ( + if size_of::<$SrcT>() <= size_of::<$DstT>() { + Some($slf as $DstT) + } else { + // Make sure the value is in range for the cast. + let n = $slf as $DstT; + let max_value: $DstT = ::std::$DstT::MAX; + if -max_value as $DstT <= n && n <= max_value as $DstT { + Some($slf as $DstT) + } else { + None + } + } + ) +} + +macro_rules! impl_to_primitive_float_to_unsigned_int { + ($SrcT:ident, $DstT:ident, $slf:expr) => ( + if size_of::<$SrcT>() <= size_of::<$DstT>() { + Some($slf as $DstT) + } else { + // Make sure the value is in range for the cast. + let n = $slf as $DstT; + let max_value: $DstT = ::std::$DstT::MAX; + if n <= max_value as $DstT { + Some($slf as $DstT) + } else { + None + } + } + ) +} + macro_rules! impl_to_primitive_float { ($T:ident) => ( impl ToPrimitive for $T { #[inline] - fn to_isize(&self) -> Option { Some(*self as isize) } + fn to_isize(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, isize, *self) + } #[inline] - fn to_i8(&self) -> Option { Some(*self as i8) } + fn to_i8(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i8, *self) + } #[inline] - fn to_i16(&self) -> Option { Some(*self as i16) } + fn to_i16(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i16, *self) + } #[inline] - fn to_i32(&self) -> Option { Some(*self as i32) } + fn to_i32(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i32, *self) + } #[inline] - fn to_i64(&self) -> Option { Some(*self as i64) } + fn to_i64(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i64, *self) + } #[inline] - fn to_usize(&self) -> Option { Some(*self as usize) } + fn to_usize(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, usize, *self) + } #[inline] - fn to_u8(&self) -> Option { Some(*self as u8) } + fn to_u8(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u8, *self) + } #[inline] - fn to_u16(&self) -> Option { Some(*self as u16) } + fn to_u16(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u16, *self) + } #[inline] - fn to_u32(&self) -> Option { Some(*self as u32) } + fn to_u32(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u32, *self) + } #[inline] - fn to_u64(&self) -> Option { Some(*self as u64) } + fn to_u64(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u64, *self) + } #[inline] - fn to_f32(&self) -> Option { impl_to_primitive_float_to_float!($T, f32, *self) } + fn to_f32(&self) -> Option { + impl_to_primitive_float_to_float!($T, f32, *self) + } #[inline] - fn to_f64(&self) -> Option { impl_to_primitive_float_to_float!($T, f64, *self) } + fn to_f64(&self) -> Option { + impl_to_primitive_float_to_float!($T, f64, *self) + } } ) } @@ -591,3 +649,17 @@ fn as_primitive() { let x: u8 = (768i16).as_(); assert_eq!(x, 0); } + +#[test] +fn float_to_integer_checks_overflow() { + // This will overflow an i32 + let source: f64 = 1.0e+123f64; + + // Expect the overflow to be caught + assert_eq!(source.to_i32(), None); + + // Specifically make sure we didn't silently let the overflow through + // (This line is mostly for humans -- the robots should catch any problem + // on the line above). + assert_ne!(source.to_i32(), Some(-2147483648)); +}