From 2c324ce3e531601d55d6da3d7eab92fbcf872884 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 20 Dec 2025 13:06:03 -0800 Subject: [PATCH] Specify error types where possible We can't name a specific error type in `PrimitiveNumber: FromStr` because it's different between floats and ints. However, we **can** refine that in `PrimitiveFloat` and `PrimitiveInteger` to specify their respective error types. Similarly, `PrimitiveInteger: TryFrom<_>` is not specific because the error could be `Infallible` or `TryFromIntError`. In the cases of `PrimitiveSigned: TryFrom` and `PrimitiveUnsigned: TryFrom` though, we do know they are always `Infallible` -- which is implied by their respective `From`, but needs to be explicit. --- src/float.rs | 3 ++- src/integer.rs | 1 + src/signed.rs | 9 +++++++- src/tests.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- src/unsigned.rs | 4 +++- 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/float.rs b/src/float.rs index 2e8979c..9035957 100644 --- a/src/float.rs +++ b/src/float.rs @@ -3,7 +3,7 @@ use crate::{PrimitiveNumber, PrimitiveNumberRef, PrimitiveUnsigned}; use core::cmp::Ordering; use core::f32::consts as f32_consts; use core::f64::consts as f64_consts; -use core::num::FpCategory; +use core::num::{FpCategory, ParseFloatError}; struct SealedToken; @@ -70,6 +70,7 @@ pub trait PrimitiveFloat: + core::convert::From + core::convert::From + core::ops::Neg + + core::str::FromStr { /// Approximate number of significant digits in base 10. const DIGITS: u32; diff --git a/src/integer.rs b/src/integer.rs index 6910c25..cec4794 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -128,6 +128,7 @@ pub trait PrimitiveInteger: + core::ops::ShrAssign + core::ops::ShrAssign + core::ops::ShrAssign + + core::str::FromStr + for<'a> core::ops::BitAnd<&'a Self, Output = Self> + for<'a> core::ops::BitAndAssign<&'a Self> + for<'a> core::ops::BitOr<&'a Self, Output = Self> diff --git a/src/signed.rs b/src/signed.rs index 91933b8..cc6aa42 100644 --- a/src/signed.rs +++ b/src/signed.rs @@ -1,3 +1,5 @@ +use core::convert::Infallible; + use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveUnsigned}; /// Trait for all primitive [signed integer types], including the supertraits [`PrimitiveInteger`] @@ -49,7 +51,12 @@ use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveUnsigned}; /// assert_eq!(extended_gcd::(1071, -462), (21, -3, -7)); /// assert_eq!(extended_gcd::(6_700_417, 2_147_483_647), (1, 715_828_096, -2_233_473)); /// ``` -pub trait PrimitiveSigned: PrimitiveInteger + From + core::ops::Neg { +pub trait PrimitiveSigned: + PrimitiveInteger + + core::convert::From + + core::convert::TryFrom + + core::ops::Neg +{ /// The unsigned integer type used by methods like [`abs_diff`][Self::abs_diff] and /// [`checked_add_unsigned`][Self::checked_add_unsigned]. type Unsigned: PrimitiveUnsigned; diff --git a/src/tests.rs b/src/tests.rs index 6b5d08f..99aba34 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -15,9 +15,14 @@ extern crate alloc; use alloc::boxed::Box; +use core::convert::Infallible; use core::error::Error; +use core::num::{ParseFloatError, ParseIntError}; -use crate::{PrimitiveError, PrimitiveInteger, PrimitiveNumber}; +use crate::{ + PrimitiveError, PrimitiveFloat, PrimitiveInteger, PrimitiveNumber, PrimitiveSigned, + PrimitiveUnsigned, +}; fn check_result<'a, T: PrimitiveNumber, E: PrimitiveError>(r: Result, ok: T) { // Cloning and equating results requires `E: Clone + PartialEq` @@ -37,22 +42,70 @@ fn check_result<'a, T: PrimitiveNumber, E: PrimitiveError>(r: Result, ok: #[test] fn parse() { + // `PrimitiveNumber` is not specific about the `FromStr` error type, + // only constraining that it implements `PrimitiveError`. fn check(s: &str, ok: T) { check_result(s.parse(), ok); } check("0", 0u32); } +#[test] +fn parse_float() { + // `PrimitiveFloat` is specific about the `FromStr` error type. + fn check(s: &str, ok: T) { + let r: Result = s.parse(); + assert_eq!(r, Ok(ok)); + } + check("0", 0f32); +} + +#[test] +fn parse_int() { + // `PrimitiveInteger` is specific about the `FromStr` error type. + fn check(s: &str, ok: T) { + let r: Result = s.parse(); + assert_eq!(r, Ok(ok)); + } + check("0", 0u32); +} + #[test] fn try_from() { + // `PrimitiveInteger` is not specific about the `TryFrom` error type, + // only constraining that it implements `PrimitiveError`. fn check(x: i32, ok: T) { check_result(T::try_from(x), ok); } check(0i32, 0u32); } +#[test] +fn try_from_signed() { + // `PrimitiveSigned` is specific that `TryFrom` is infallible. + // (implied by `From`, but we still need an explicit constraint) + fn check(x: i8, ok: T) { + let r: Result = T::try_from(x); + assert_eq!(r, Ok(ok)); + } + check(0i8, 0i32); +} + +#[test] +fn try_from_unsigned() { + // `PrimitiveUnsigned` is specific that `TryFrom` is infallible. + // (implied by `From`, but we still need an explicit constraint) + fn check(x: u8, ok: T) { + let r: Result = T::try_from(x); + assert_eq!(r, Ok(ok)); + } + check(0u8, 0u32); +} + #[test] fn try_into() { + // `PrimitiveInteger` is not specific about the `TryInto` error type, + // only constraining that it implements `PrimitiveError`. fn check(x: T, ok: u32) { check_result(x.try_into(), ok); } diff --git a/src/unsigned.rs b/src/unsigned.rs index 4f7592e..6bb21b9 100644 --- a/src/unsigned.rs +++ b/src/unsigned.rs @@ -1,3 +1,5 @@ +use core::convert::Infallible; + use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveSigned}; /// Trait for all primitive [unsigned integer types], including the supertraits @@ -32,7 +34,7 @@ use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveSigned}; /// assert_eq!(gcd::(1071, 462), 21); /// assert_eq!(gcd::(6_700_417, 2_147_483_647), 1); /// ``` -pub trait PrimitiveUnsigned: PrimitiveInteger + From { +pub trait PrimitiveUnsigned: PrimitiveInteger + From + TryFrom { /// The signed integer type used by methods like /// [`checked_add_signed`][Self::checked_add_signed]. type Signed: PrimitiveSigned;