std::num::TryFromIntError - why is it so private / can it be expanded?

Right now, std::num::TryFromIntError is a bit of a weird error type. It is used for when converting between two numeric types fails, e.g. trying to convert 256_u16 to a u8. It was introduced in Implement RFC 1542 by sfackler · Pull Request #33426 · rust-lang/rust · GitHub when implementing this tracking issue https://github.com/rust-lang/rust/issues/33417 of this RFC: Add TryFrom and TryInto traits by sfackler · Pull Request #1542 · rust-lang/rfcs · GitHub

The error type doesn't store the value which failed to convert (and doesn't have a generic type to be able to do so).

It also is a tuple struct with a private field, which prevents being able to construct this value outside of std.

Is there a reason it's privately constructable? Is it to leave the option open to expand its implementation? Or because it's a poor choice for an error type outside of std? Or something else?

I maintain a crate which has a similar error type but which has a generic field which tracks the value which couldn't be converted.

I'm trying to use the transitive crate to derive transitive conversions which include both of these error types, which requires being able to convert between these via From impls.

Unfortunately, the design of std::num::TryFromIntError seems to make this impossible. I can't convert from my type to the std one because I can't construct the one in std (because its constructor is private). And I can't convert from the std one to my one because it expects to be able to access information which it would be reasonable to expect to be able to find on the std error, but which it doesn't happen to hold.

On the original tracking issue the ability to get back the converted type was asked about a few times, but never answered.

I'd quite like to add this ability in retrospect, but that seems tricky because it would add a generic parameter which would be a breaking change (which... Maybe through some magic could happen over an edition boundary?) - is this reasonably possible? And if not, should we accept that the type is effectively locked down anyway, and allow it to be publicly constructed?

6 Likes

I can't comment on why it has a private field, but would just note that there is a reasonably easy workaround to get a value of that type (without using transmute).

You can get it simply by doing a conversion that is guaranteed to fail, and then unwrapping the error. With an optimisation level other than 0, it compiles to nothing. (Compiler Explorer)

fn get_try_from_int_error() -> TryFromIntError {
    <u8 as TryFrom<u16>>::try_from(300).unwrap_err()
}
7 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.