-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TryFrom implementation for bool, f32 and f64 #43220
Conversation
src/libcore/num/mod.rs
Outdated
type Error = (); | ||
|
||
fn try_from(u: $source) -> Result<bool, ()> { | ||
Ok(u != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there precedent in Rust for this being the canonical way (or even "a" right way) of turning integers into bools? For example, there's no as
cast from integers to bools, no From
impl, no num::NumCast
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not as far as I know. I just copied the C behavior.
src/libcore/num/mod.rs
Outdated
same_sign_try_from_int_impl!(i128, i128, i8, i16, i32, i64, i128, isize); | ||
same_sign_try_from_int_impl!(u128, usize, u8, u16, u32, u64, u128, usize); | ||
same_sign_try_from_int_impl!(i128, isize, i8, i16, i32, i64, i128, isize); | ||
same_sign_try_from_int_impl!(u128, u8, u8, u16, u32, u64, u128, usize, f32, f64); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I read the macro right, this will cast fN::{MIN,MAX}
to {u,i}128? That will overflow for all combinations except f32 -> i128.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the macro is getting the max for the source
value (the one you implement the trait on). Then you cast the target
to the source
and make the comparison. So it cannot overflow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/libcore/num/mod.rs
Outdated
@@ -2504,6 +2504,30 @@ impl fmt::Display for TryFromIntError { | |||
} | |||
} | |||
|
|||
#[unstable(feature = "try_from", issue = "33417")] | |||
impl TryFrom<bool> for bool { | |||
type Error = (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Error type shouldn't be ()
. Probably TryFromBoolError
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should really be !
or some other uninhabited type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(since the conversion is infallible)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The real solution to this is an eventual impl<A, B> TryFrom<A> for B where B: From<A>
with type Error = !;
, but that's blocked for several reasons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this one should be covered by the (not yet existing) blanket impl impl<T, U> TryFrom<T> for U where U: From<T>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what you should I do in here? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GuillaumeGomez Create something like std::string::ParseError
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd advise to leave these unimplemented entirely until the blanket impl comes-- otherwise the blanket impl will be a breaking change (changing the error type from your custom uninhabited type to !
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cramertj Breaking change for an unstable feature is fine 😃. (Although I recommend the entire PR be held off 😛.) The blanket impl still won't use !
directly to avoid being blocked by never_type
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see-- yeah, you could just backwards-compatibly change your custom type into a type alias to !
. The reason I prefer !
is that it has coercions that custom enums don't have, although I suppose those might be added someday.
src/libcore/num/mod.rs
Outdated
same_sign_try_from_int_impl!(i128, i32, i8, i16, i32, i64, i128, isize, f32, f64); | ||
same_sign_try_from_int_impl!(u128, u64, u8, u16, u32, u64, u128, usize, f32, f64); | ||
same_sign_try_from_int_impl!(i128, i64, i8, i16, i32, i64, i128, isize, f32, f64); | ||
same_sign_try_from_int_impl!(u128, u128, u8, u16, u32, u64, u128, usize, f32, f64); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe these will trigger #10184 ("float as int" can be UB — consider u8::try_from(1.0e+100_f64)
which will involve a (1.0e+100_f64 as u128) > 255
check in the middle).
They should use the float types themselves as $storage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll handle this cast differently then.
src/libcore/num/mod.rs
Outdated
|
||
let min = $target::MIN as $storage; | ||
let max = $target::MAX as $storage; | ||
if u as $storage < min || u as $storage > max { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These basically rules out conversion of float::INFINITY
and float::NAN
, i.e. Ok(f64::from(f32::INFINITY))
and f64::try_from(f32::INFINITY)
will produce different result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent point! I'll add a special case for Infinity
and NaN
.
It's fine as is. I fix the current issues and then it's open until... |
impl TryFrom<bool> for bool { | ||
type Error = TryFromBoolError; | ||
|
||
fn try_from(u: bool) -> Result<bool, TryFromBoolError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want to add [inline] here. At least for the integer types, the calls to try_from() didn't inline properly without having [inline], so it might be similar for bool/float.
Hmm. As this touches the standard library, I'm going to guess this is a libs thing and randomly pick... r? @BurntSushi |
The The conversions for bools seem more controversial to me. Do we have any precedent for those conversions anywhere else? What is the use case for having them? |
@BurntSushi We want to have a blanket impl impl<T: From<U>> TryFrom<U> for T {
type Error = !; // <-- or an empty enum before `!` is stable
fn try_from(u: U) -> Result<Self, Self::Error> {
Ok(Self::from(u))
}
} Since It is the same for floats, The plan as of June 6th was to remove |
ping r? @sfackler |
friendly ping for review @sfackler @rust-lang/libs! |
The I'm not sure how I feel about the float impls. Arguably the implementation should fail if the number can't be represented exactly as the float type? |
☔ The latest upstream changes (presumably #43248) made this pull request unmergeable. Please resolve the merge conflicts. |
@GuillaumeGomez do you hae thoughts on @sfackler's recent comment? |
The try_from impls should probably be marked inline like the existing ones. |
I'll update my PR this week-end. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is much reason to continue this PR. The int
to bool
conversion is completely arbitrary and if you need != 0
then that's what you should use not .try_into().unwrap()
. Even if the float conversions had the correct bounds, had int to float and had tests there is still the question of whether TryFrom
should allow rounding. We have impl From<i32> for f64
but not impl From<i32> for f32
because even though the bounds of i32
fit well inside the bounds of f32
, you can lose precision.
same_sign_try_from_int_impl!(f64, i16, f32, f64); | ||
same_sign_try_from_int_impl!(f64, u16, f32, f64); | ||
same_sign_try_from_int_impl!(f64, i8, f32, f64); | ||
same_sign_try_from_int_impl!(f64, u8, f32, f64); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For what it's worth the bounds for these conversions are wrong. NaN needs to be taken into account. Also float to int conversions round towards 0 so for example -0.9 as u8 == 0
and 255.9 as u8 == 255
so the bounds need to allow that.
} else { | ||
let min = $target::MIN as $storage; | ||
let max = $target::MAX as $storage; | ||
if u as $storage < min || u as $storage > max { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These bounds are also wrong for f64
to f32
which is the only conversion that actually needs a manual TryFrom
conversion. The source value is rounded when casting to f32
so some values larger than f32::MAX as f64
will round down to f32::MAX
. How fptrunc
performs this rounding is apparently undefined so I'm not completely sure what the bounds will need to be though.
Just like @ollie27, I think this PR has no more interest. I'm closing it. |
Fixes #43214.