Auto merge of #147784 - nxsaken:div_exact_return_option, r=dtolnay

Return `Option` from `exact_div` and inherit overflow checks

According to https://github.com/rust-lang/rust/issues/139911#issuecomment-3404056127, `exact_div` should return `Option::None` if `self % rhs != 0`, panic if `rhs == 0`, and handle overflow conditionally (panic in debug, wrap in release).

rust-lang/rust#147771 should rename `exact_div` to `div_exact`.
This commit is contained in:
bors 2025-11-02 08:05:11 +00:00
commit 73e6c9ebd9
4 changed files with 37 additions and 31 deletions

View file

@ -1019,25 +1019,29 @@ macro_rules! int_impl {
}
}
/// Checked integer division without remainder. Computes `self / rhs`.
/// Integer division without remainder. Computes `self / rhs`, returning `None` if `self % rhs != 0`.
///
/// # Panics
///
/// This function will panic if `rhs == 0`, the division results in overflow,
/// or `self % rhs != 0`.
/// This function will panic if `rhs == 0`.
///
/// ## Overflow behavior
///
/// On overflow, this function will panic if overflow checks are enabled (default in debug
/// mode) and wrap if overflow checks are disabled (default in release mode).
///
/// # Examples
///
/// ```
/// #![feature(exact_div)]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).exact_div(-1), ", stringify!($Max), ");")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), Some(32));")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), Some(2));")]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).exact_div(-1), Some(", stringify!($Max), "));")]
#[doc = concat!("assert_eq!(65", stringify!($SelfT), ".exact_div(2), None);")]
/// ```
///
/// ```should_panic
/// #![feature(exact_div)]
#[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")]
#[doc = concat!("let _ = 64", stringify!($SelfT),".exact_div(0);")]
/// ```
/// ```should_panic
/// #![feature(exact_div)]
@ -1050,10 +1054,12 @@ macro_rules! int_impl {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn exact_div(self, rhs: Self) -> Self {
match self.checked_exact_div(rhs) {
Some(x) => x,
None => panic!("Failed to divide without remainder"),
#[rustc_inherit_overflow_checks]
pub const fn exact_div(self, rhs: Self) -> Option<Self> {
if self % rhs != 0 {
None
} else {
Some(self / rhs)
}
}

View file

@ -1249,23 +1249,19 @@ macro_rules! uint_impl {
}
}
/// Checked integer division without remainder. Computes `self / rhs`.
/// Integer division without remainder. Computes `self / rhs`, returning `None` if `self % rhs != 0`.
///
/// # Panics
///
/// This function will panic if `rhs == 0` or `self % rhs != 0`.
/// This function will panic if `rhs == 0`.
///
/// # Examples
///
/// ```
/// #![feature(exact_div)]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")]
/// ```
///
/// ```should_panic
/// #![feature(exact_div)]
#[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), Some(32));")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), Some(2));")]
#[doc = concat!("assert_eq!(65", stringify!($SelfT), ".exact_div(2), None);")]
/// ```
#[unstable(
feature = "exact_div",
@ -1274,10 +1270,12 @@ macro_rules! uint_impl {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn exact_div(self, rhs: Self) -> Self {
match self.checked_exact_div(rhs) {
Some(x) => x,
None => panic!("Failed to divide without remainder"),
#[rustc_inherit_overflow_checks]
pub const fn exact_div(self, rhs: Self) -> Option<Self> {
if self % rhs != 0 {
None
} else {
Some(self / rhs)
}
}

View file

@ -741,22 +741,23 @@ macro_rules! int_module {
fn test_exact_div() {
// 42 / 6
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1));
assert_eq_const_safe!($T: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), EXACT_DIV_SUCCESS_QUOTIENT1);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1));
// 18 / 3
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2));
assert_eq_const_safe!($T: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), EXACT_DIV_SUCCESS_QUOTIENT2);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2));
// -91 / 13
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND3, EXACT_DIV_SUCCESS_DIVISOR3), Some(EXACT_DIV_SUCCESS_QUOTIENT3));
assert_eq_const_safe!($T: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND3, EXACT_DIV_SUCCESS_DIVISOR3), EXACT_DIV_SUCCESS_QUOTIENT3);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND3, EXACT_DIV_SUCCESS_DIVISOR3), Some(EXACT_DIV_SUCCESS_QUOTIENT3));
// -57 / -3
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND4, EXACT_DIV_SUCCESS_DIVISOR4), Some(EXACT_DIV_SUCCESS_QUOTIENT4));
assert_eq_const_safe!($T: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND4, EXACT_DIV_SUCCESS_DIVISOR4), EXACT_DIV_SUCCESS_QUOTIENT4);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND4, EXACT_DIV_SUCCESS_DIVISOR4), Some(EXACT_DIV_SUCCESS_QUOTIENT4));
// failures
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(1, 2), None);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(1, 2), None);
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(<$T>::MIN, -1), None);
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(0, 0), None);
}

View file

@ -606,14 +606,15 @@ macro_rules! uint_module {
fn test_exact_div() {
// 42 / 6
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1));
assert_eq_const_safe!($T: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), EXACT_DIV_SUCCESS_QUOTIENT1);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1));
// 18 / 3
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2));
assert_eq_const_safe!($T: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), EXACT_DIV_SUCCESS_QUOTIENT2);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2));
// failures
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(1, 2), None);
assert_eq_const_safe!(Option<$T>: <$T>::exact_div(1, 2), None);
assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(0, 0), None);
}
}