-
Notifications
You must be signed in to change notification settings - Fork 24
Description
Proposal
Problem statement
TryFromIntError does not provide detailed information about the reason for an integral type conversion failure. For example, converting an i64 value to an i8 value will fail if the result is smaller than i8::MIN or larger than i8::MAX, but TryFromIntError cannot determine which is the cause of the failure.
Motivating examples or use cases
The nt-time crate I created provides methods to convert Unix time represented as a signed integer to a Windows file time. These methods perform integer type conversions internally, such as from i128 to u64, but if we can obtain the detailed cause of TryFromIntError, we can implement conversion from the TryFromIntError to this crate's error type. If this can be implemented, it will be useful to reduce the use of Result::map_err.
impl From<TryFromIntError> for FileTimeRangeError {
fn from(error: TryFromIntError) -> Self {
match error.kind() {
IntErrorKind::PosOverflow => Self(FileTimeRangeErrorKind::Overflow),
IntErrorKind::NegOverflow => Self(FileTimeRangeErrorKind::Negative),
_ => unreachable!(),
}
}
}Solution sketch
// core::num
impl TryFromIntError {
/// Outputs the detailed cause of converting an integer failing.
#[must_use]
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
pub const fn kind(&self) -> &IntErrorKind {
&self.0
}
}
pub enum IntErrorKind {
...
/// Value is not a power of two.
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
NotAPowerOfTwo,
}Since the Copy trait has been implemented for IntErrorKind since Rust 1.90.0, I think the return type of this method can be IntErrorKind instead of &IntErrorKind. However, I think it would be better to align it with ParseIntError::kind, so I plan to use &IntErrorKind as the return type.
NotAPowerOfTwo is a variant for impl TryFrom<usize> for Alignment. This returns TryFromIntError as an error if the given value is not a power of two. However, IntErrorKind does not have an appropriate variant to represents this. So I suggest adding a variant to represent this.
To maintain compatibility, the error message of TryFromIntError remains unchanged.
Currently, IntErrorKind is used to show the details of ParseIntError. I think this can also be used to show the details of TryFromIntError. If this is inappropriate, define TryFromIntErrorKind instead.
Definition of TryFromIntErrorKind
/// Enum to store the various types of errors that can cause converting an integer to fail.
///
/// # Example
///
/// ```
/// # fn main() {
/// if let Err(e) = i32::try_from(i64::MAX) {
/// println!("Failed conversion to i32: {:?}", e.kind());
/// }
/// # }
/// ```
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum TryFromIntErrorKind {
/// Integer is too large to store in target integer type.
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
PosOverflow,
/// Integer is too small to store in target integer type.
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
NegOverflow,
/// Value was Zero
///
/// This variant will be emitted when the converting integer has a value of zero, which
/// would be illegal for non-zero types.
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
Zero,
/// Value is not a power of two.
#[unstable(feature = "try_from_int_error_kind", issue = "none")]
NotAPowerOfTwo,
}Alternatives
Convert an integer to a string and parse it to get the detailed reason for the failure:
let s = (u8::MAX as u16 + 1).to_string();
assert_eq!(
s.parse::<u8>().unwrap_err().kind(),
&IntErrorKind::PosOverflow
);However, this method is redundant because it requires converting an integer to a string once.
Also, when converting a negative integer of a signed integer type to an unsigned integer type (e.g., from i64::MIN to u32), the value of IntErrorKind is IntErrorKind::InvalidDigit, so it is difficult to understand that the conversion failed because the result was too small.
Links and related work
None.
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.