From 352b5ba32b461fd73af19b695de2d2bd7845b549 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Mon, 3 Aug 2026 11:25:02 +0000 Subject: [PATCH 1/2] Make Signed::abs match its documented behavior for ::MIN The trait documentation states: "For signed integers, ::MIN will be returned if the number is ::MIN." The implementation used unary negation, which panics with 'attempt to negate with overflow' when overflow checks are enabled (debug builds), so the documented behavior only held in release builds. Use wrapping_abs, which implements the documented semantics in both profiles. Also document the overflow behavior of abs_sub for signed integers (e.g. MAX.abs_sub(&-1) is not representable): it panics with overflow checks enabled and wraps otherwise. Found by running Kani's autoharness (model-checking/kani#3832) over num-traits: the generated harness for abs:: reported the negate overflow reachable at sign.rs. Co-authored-by: Kiro --- src/sign.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sign.rs b/src/sign.rs index a0d6b0fd..afaef433 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -17,6 +17,10 @@ pub trait Signed: Sized + Num + Neg { /// /// Returns `zero` if the number is less than or equal to `other`, otherwise the difference /// between `self` and `other` is returned. + /// + /// For signed integers, the difference may not be representable (e.g. + /// `MAX.abs_sub(&-1)`), in which case the subtraction overflows: it panics when overflow + /// checks are enabled and wraps around otherwise. fn abs_sub(&self, other: &Self) -> Self; /// Returns the sign of the number. @@ -46,7 +50,9 @@ macro_rules! signed_impl { impl Signed for $t { #[inline] fn abs(&self) -> $t { - if self.is_negative() { -*self } else { *self } + // `wrapping_abs` matches the documented behavior (`::MIN` is returned for + // `::MIN`); negation would panic on `::MIN` when overflow checks are enabled. + self.wrapping_abs() } #[inline] @@ -214,3 +220,16 @@ fn signed_wrapping_is_signed() { fn require_signed(_: &T) {} require_signed(&Wrapping(-42)); } + +#[test] +fn abs_min_returns_min() { + // The documented behavior: "For signed integers, `::MIN` will be returned if the number + // is `::MIN`." This must also hold when overflow checks are enabled. + assert_eq!(Signed::abs(&isize::MIN), isize::MIN); + assert_eq!(Signed::abs(&i8::MIN), i8::MIN); + assert_eq!(Signed::abs(&i16::MIN), i16::MIN); + assert_eq!(Signed::abs(&i32::MIN), i32::MIN); + assert_eq!(Signed::abs(&i64::MIN), i64::MIN); + assert_eq!(Signed::abs(&i128::MIN), i128::MIN); + assert_eq!(crate::sign::abs(i32::MIN), i32::MIN); +} From 4a06bc4d1cc0db063a1c5faa484187d43bc7a51d Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 5 Aug 2026 09:35:49 +0000 Subject: [PATCH 2/2] Use a concrete type in the abs_sub doc example Review feedback: MAX.abs_sub(&-1) is ambiguous without a concrete integer type; use i32::MAX so the example can be read (and copied) unmodified. Co-authored-by: Kiro --- src/sign.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sign.rs b/src/sign.rs index afaef433..27222051 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -19,8 +19,8 @@ pub trait Signed: Sized + Num + Neg { /// between `self` and `other` is returned. /// /// For signed integers, the difference may not be representable (e.g. - /// `MAX.abs_sub(&-1)`), in which case the subtraction overflows: it panics when overflow - /// checks are enabled and wraps around otherwise. + /// `i32::MAX.abs_sub(&-1)`), in which case the subtraction overflows: it panics when + /// overflow checks are enabled and wraps around otherwise. fn abs_sub(&self, other: &Self) -> Self; /// Returns the sign of the number.