Skip to content
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

Merged
merged 13 commits into from
Sep 30, 2017
Merged

Add blanket TryFrom impl when From is implemented. #44174

merged 13 commits into from
Sep 30, 2017

Conversation

jimmycuadra
Copy link
Contributor

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

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.
@rust-highfive
Copy link
Collaborator

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.

Copy link
Member

@scottmcm scottmcm left a 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!

impl TryFrom<$source> for $target {
type Error = TryFromIntError;

impl From<$source> for $target {
Copy link
Member

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> {
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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>
Copy link
Member

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`

/// 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 {}
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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);
Copy link
Member

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 🎉

@alexcrichton
Copy link
Member

r? @sfackler

Seems fine to me, but I think @sfackler is a little more "in the loop" here

@aidanhs aidanhs added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 31, 2017
use str::FromStr;
use fmt;

/// An uninhabited type used as the error type for implementations of fallible
Copy link
Contributor

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)]
Copy link
Contributor

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?

Copy link
Member

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 $ts (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');
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@clarfonthey
Copy link
Contributor

clarfonthey commented Aug 31, 2017

I would very much like to oppose reverting the FromStr -> TryFrom switch because the latter allows parse errors to depend on the lifetime of the string, allowing for slices from the string to be contained in the error.

Although the overlap is quite a problem. Perhaps we could use specialisation to get around it? If we default the FromStr impl it should work, I think. I don't mind preferring From<&str> to FromStr.

Of course, this part of specialisation isn't implemented afaik...

Maybe in the meantime we could use clippy to suggest implementing TryFrom when FromStr is implemented, then after specialisation make the switch.

/// 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 {}
Copy link
Member

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;
Copy link
Member

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...

Copy link
Contributor Author

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?

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

@jimmycuadra
Copy link
Contributor Author

@clarcharr

I completely agree with you that removing the FromStr --> TryFrom impl is undesirable. I did it because the new blanket impl conflicts with that one, as you suspected: https://gist.github.com/jimmycuadra/f59871d00741a8445f17419485d0ba29

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.

@clarfonthey
Copy link
Contributor

Now that I've thought about it, I think that the best long-term solution would be to deprecate (or at least soft deprecate) str::parse in favour of TryFrom::try_from. This impl is much more important and honestly, the hack of FromStr -> TryFrom wasn't going to last long-term anyway.

@jimmycuadra
Copy link
Contributor Author

Works for me. I can submit another PR deprecating parse and/or FromStr as soon as we stabilize try_from.

@arielb1 arielb1 added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 5, 2017
@carols10cents
Copy link
Member

@jimmycuadra What's the status of this PR? I'm having a hard time telling.

@jimmycuadra
Copy link
Contributor Author

@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 FromStr.

/// 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 {}
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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).

Copy link
Contributor

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.

@carols10cents carols10cents added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 12, 2017
@jimmycuadra
Copy link
Contributor Author

@sfackler Made a few more changes. I think everything is good now.

@aidanhs
Copy link
Member

aidanhs commented Sep 28, 2017

Pinging @sfackler for review (and also on IRC)!

#[unstable(feature = "try_from", issue = "33417")]
impl From<Infallible> for TryFromIntError {
fn from(_: Infallible) -> TryFromIntError {
TryFromIntError(())
Copy link
Member

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 {}.

@sfackler
Copy link
Member

There's one other little nit, but it doesn't need to block this any longer :)

@bors r+

@bors
Copy link
Contributor

bors commented Sep 28, 2017

📌 Commit ba74a86 has been approved by sfackler

@bors
Copy link
Contributor

bors commented Sep 28, 2017

⌛ Testing commit ba74a86 with merge b63d442ac1112da695b5862fe6e9cec2a88a27fd...

@bors
Copy link
Contributor

bors commented Sep 28, 2017

💔 Test failed - status-appveyor

@kennytm
Copy link
Member

kennytm commented Sep 28, 2017

Legit. Looks like there's conflict about impl TryFrom<u8> for usize (also u16usize, i8isize and i16isize) on i686-pc-windows-msvc.

error[E0520]: `Error` specializes an item from a parent `impl`, but that item is not marked `default`
    --> src\libcore\num\mod.rs:2522:13
     |
2522 |               type Error = Infallible;
     |               ^^^^^^^^^^^^^^^^^^^^^^^^ cannot specialize default item `Error`
...
2671 |       rev!(try_from_unbounded, usize, u8, u16, u32);
     |       ---------------------------------------------- in this macro invocation
     | 
    ::: src\libcore\convert.rs
     |
440  | / impl<T, U> TryFrom<U> for T where T: From<U> {
441  | |     type Error = Infallible;
442  | |
443  | |     fn try_from(value: U) -> Result<Self, Self::Error> {
444  | |         Ok(T::from(value))
445  | |     }
446  | | }
     | |_- parent `impl` is here
     |
     = note: to specialize, `Error` in the parent `impl` must be marked `default`

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);
Copy link
Member

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.

@jimmycuadra
Copy link
Contributor Author

I addressed @ollie27's comment and fixed the nit from @sfackler's previous comment.

@sfackler
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Sep 29, 2017

📌 Commit 27d95d3 has been approved by sfackler

@bors
Copy link
Contributor

bors commented Sep 29, 2017

⌛ Testing commit 27d95d3 with merge b7041bf...

bors added a commit that referenced this pull request Sep 29, 2017
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
@bors
Copy link
Contributor

bors commented Sep 30, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: sfackler
Pushing b7041bf to master...

@arthurprs
Copy link
Contributor

Losing the FromStr impl is sad 😭 but I have no suggestions to fix.

@clarfonthey
Copy link
Contributor

IMHO try_into should become the more idiomatic way to do parse anyway.

@arthurprs
Copy link
Contributor

They're the exact same, I'd be happy to see FromStr deprecated eventually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.