-
Notifications
You must be signed in to change notification settings - Fork 13k
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 blanket TryFrom impl when From is implemented. #44174
Conversation
Adds `impl<T, U> TryFrom<T> for U where U: From<T>`. Removes `impl<'a, T> TryFrom<&'a str> for T where T: FromStr` due to overlapping impls caused by the new blanket impl. This removal is to be discussed further on the tracking issue for TryFrom. Refs #33417.
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @alexcrichton (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
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.
Thanks for taking this on, @jimmycuadra!
src/libcore/num/mod.rs
Outdated
impl TryFrom<$source> for $target { | ||
type Error = TryFromIntError; | ||
|
||
impl From<$source> for $target { |
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 think this needs to stay TryFrom, at least for now, since this would be insta-stable and I don't think libs wants it until the portability lint RFC is available (if ever).
// Infallible conversions are semantically equivalent to fallible conversions | ||
// with an uninhabited error type. | ||
#[unstable(feature = "try_from", issue = "33417")] | ||
impl<T, U> TryFrom<U> for T where T: From<U> { |
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 have no idea if it's better or worse, but could this be on U:Into<T>
, to pick up more impls?
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.
Sounds fine to me. I'd be happy to change it to that if everyone is in agreement to do it.
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 rather not simply because it'd be confusing for from
to not work but try_from
to work in those cases.
@@ -442,14 +470,3 @@ impl AsRef<str> for str { | |||
self | |||
} | |||
} | |||
|
|||
// FromStr implies TryFrom<&str> |
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.
Looks like this broke something in Travis:
[00:53:44] error[E0277]: the trait bound `char: core::convert::From<&str>` is not satisfied
[00:53:44] --> /checkout/src/libcore/../libcore/tests/char.rs:35:16
[00:53:44] |
[00:53:44] 35 | assert_eq!(char::try_from("a").unwrap(), 'a');
[00:53:44] | ^^^^^^^^^^^^^^ the trait `core::convert::From<&str>` is not implemented for `char`
[00:53:44] |
[00:53:44] = help: the following implementations were found:
[00:53:44] <char as core::convert::From<u8>>
[00:53:44] = note: required because of the requirements on the impl of `core::convert::TryFrom<&str>` for `char`
src/libcore/convert.rs
Outdated
/// an error type, and signals to both the compiler and the user that the error | ||
/// case is impossible. | ||
#[unstable(feature = "try_from", issue = "33417")] | ||
pub enum 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.
Ideally this could become !
at some point. Is that the kind of thing that goes in comments, or only in tracking issues?
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 thought about it, but assuming TryFrom stabilizes first and this ends up in the stable docs, I wouldn't want to explain it in terms of an unstable feature that could change or theoretically go away completely. When the never type lands, it'll simply the explanation here, though.
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'm not too fond of this; maybe we could rename it NeverError
or something similar to add Error
to the name?
Also, I'd rather this implement Error
too.
try_from_unbounded!(i16, i16, i32, i64, i128); | ||
try_from_unbounded!(i32, i32, i64, i128); | ||
try_from_unbounded!(i64, i64, i128); | ||
try_from_unbounded!(i128, 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.
Nice to see all these go away 🎉
src/libcore/convert.rs
Outdated
use str::FromStr; | ||
use fmt; | ||
|
||
/// An uninhabited type used as the error type for implementations of fallible |
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 think that adding "uninhabited" here might confuse new users. The latter paragraph explains better so I think we can just omit it?
@@ -89,6 +89,7 @@ macro_rules! step_impl_unsigned { | |||
} | |||
|
|||
#[inline] | |||
#[allow(unreachable_patterns)] |
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.
Why not just remove the Err
branch?
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.
Because it's only unreachable for certain $t
s (like u128
), not all of them (like u8
).
@@ -32,7 +32,6 @@ fn test_convert() { | |||
#[test] | |||
fn test_from_str() { | |||
assert_eq!(char::from_str("a").unwrap(), 'a'); | |||
assert_eq!(char::try_from("a").unwrap(), 'a'); |
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.
Why was this test removed? Shouldn't it succeed?
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.
This was because of the FromStr
--> TryFrom
impl being removed. The test fails without it.
I would very much like to oppose reverting the Although the overlap is quite a problem. Perhaps we could use specialisation to get around it? If we Of course, this part of specialisation isn't implemented afaik... Maybe in the meantime we could use clippy to suggest implementing |
src/libcore/convert.rs
Outdated
/// an error type, and signals to both the compiler and the user that the error | ||
/// case is impossible. | ||
#[unstable(feature = "try_from", issue = "33417")] | ||
pub enum 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.
This comment reminded me of something: Can you please add an impl From<Infallible> for TryFromIntError
? That way code like this will compile even if the try_into is actually infallible:
fn foo(x: c_int) -> Result<i32, TryFromIntError> { Ok(x.try_into()?) }
@@ -2508,11 +2508,11 @@ macro_rules! try_from_unbounded { | |||
($source:ty, $($target:ty),*) => {$( | |||
#[unstable(feature = "try_from", issue = "33417")] | |||
impl TryFrom<$source> for $target { | |||
type Error = TryFromIntError; | |||
type Error = 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.
Hmm, this might need to remain TryFromIntError
to avoid portability hazards...
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.
Isn't this macro only being called when the target's pointer width makes the conversion 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.
Is this macro used anymore?
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.
Yes, it's used in the target-specific ptr_try_from_impls
module.
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 we want these impls changing based on your target.
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.
Most calls to this macro were removed in this PR. The ones that are still there are the conversions from specifically sized integer types to usize
/isize
. For example, u32
is only an infallible conversion to usize
if the target's pointer width is 32 or higher.
@clarcharr I completely agree with you that removing the I tried using specialization to get around it by making the new blanket impl the default, but it didn't change the compiler error. After discussing with @scottmcm on IRC, we thought we'd try temporarily removing it and then discussing what to do about it more on the tracking issue. Personally, I'm also fine in having some/all of that discussion here, especially if we can find a way for the two impls to coexist before merging this PR. |
Now that I've thought about it, I think that the best long-term solution would be to deprecate (or at least soft deprecate) |
Works for me. I can submit another PR deprecating |
@jimmycuadra What's the status of this PR? I'm having a hard time telling. |
@carols10cents Just waiting for merge, as far as I can see, unless anyone on the libs team wants to go a different way for dealing with |
src/libcore/convert.rs
Outdated
/// It is used only to satisfy trait signatures that expect an error type, and | ||
/// signals to both the compiler and the user that the error case is impossible. | ||
#[unstable(feature = "try_from", issue = "33417")] | ||
pub enum 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.
I'm still too not sold on the name Infalliable
; perhaps NeverError
or something similar would be better?
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.
We also should have more impls for this, like Eq
, PartialEq
, Ord
, PartialOrd
, Hash
, and 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.
Agreed on the extra derived impls. I will add those. NeverError
probably goes against the style guidelines around "stutter" in names. I'd be open to just Never
, though. After all, that's the way you say !
(as a 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.
After thinking about it, maybe CannotError
? I think it should probably end in Error
by convention.
@sfackler Made a few more changes. I think everything is good now. |
Pinging @sfackler for review (and also on IRC)! |
src/libcore/num/mod.rs
Outdated
#[unstable(feature = "try_from", issue = "33417")] | ||
impl From<Infallible> for TryFromIntError { | ||
fn from(_: Infallible) -> TryFromIntError { | ||
TryFromIntError(()) |
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.
This might also be a bit more clearly expressed as match value {}
.
There's one other little nit, but it doesn't need to block this any longer :) @bors r+ |
📌 Commit ba74a86 has been approved by |
⌛ Testing commit ba74a86 with merge b63d442ac1112da695b5862fe6e9cec2a88a27fd... |
💔 Test failed - status-appveyor |
Legit. Looks like there's conflict about
|
src/libcore/num/mod.rs
Outdated
rev!(try_from_upper_bounded, usize, u64, u128); | ||
rev!(try_from_lower_bounded, usize, i8, i16, i32); | ||
rev!(try_from_both_bounded, usize, i64, i128); | ||
|
||
rev!(try_from_unbounded, isize, u8, u16); |
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.
This impl needs to remain, it's the i8
to isize
impl below that needs to be removed. The same two impls need to be removed for the 16-bit case as well. Basically ba74a86 needs to be reverted.
@bors r+ |
📌 Commit 27d95d3 has been approved by |
Add blanket TryFrom impl when From is implemented. Adds `impl<T, U> TryFrom<T> for U where U: From<T>`. Removes `impl<'a, T> TryFrom<&'a str> for T where T: FromStr` (originally added in #40281) due to overlapping impls caused by the new blanket impl. This removal is to be discussed further on the tracking issue for TryFrom. Refs #33417. /cc @sfackler, @scottmcm (thank you for the help!), and @aturon
☀️ Test successful - status-appveyor, status-travis |
Losing the FromStr impl is sad 😭 but I have no suggestions to fix. |
IMHO |
They're the exact same, I'd be happy to see FromStr |
Adds
impl<T, U> TryFrom<T> for U where U: From<T>
.Removes
impl<'a, T> TryFrom<&'a str> for T where T: FromStr
(originally added in #40281) due to overlapping impls caused by the new blanket impl. This removal is to be discussed further on the tracking issue for TryFrom.Refs #33417.
/cc @sfackler, @scottmcm (thank you for the help!), and @aturon