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 TryFrom<{integer}> for bool #50597

Conversation

@ithinuel
Copy link

ithinuel commented May 10, 2018

This adds the conversion from and into boolean for integer types.

This is also discused here : https://internals.rust-lang.org/t/from-bool-for-primitive-integer-types

@rust-highfive

This comment has been minimized.

Copy link
Collaborator

rust-highfive commented May 10, 2018

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @shepmaster (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.

impl_from! { i32, bool, #[unstable(feature = "into_bool", issue = "0")]}
impl_from! { i64, bool, #[unstable(feature = "into_bool", issue = "0")]}
impl_from! { i128, bool, #[unstable(feature = "into_bool", issue = "0")]}
// Boolean -> Integer

This comment has been minimized.

@scottmcm

scottmcm May 10, 2018

Member

There's a PR open for this part at #50554

This comment has been minimized.

@ithinuel

ithinuel May 10, 2018

Author

😮 I'm few hours late

This comment has been minimized.

@ithinuel

ithinuel May 10, 2018

Author

I updated the issue number to #46109

@@ -4580,6 +4602,29 @@ impl_from! { u32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0"
// Float -> Float
impl_from! { f32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }

// Integer -> Boolean

This comment has been minimized.

@scottmcm

scottmcm May 10, 2018

Member

This is lossy, so feels questionable whether it should be there in From.

This comment has been minimized.

@ithinuel

ithinuel May 10, 2018

Author

I use from here but I can change to TryFrom if required.

This comment has been minimized.

@ithinuel

ithinuel May 10, 2018

Author

Updated to use TryFrom instead.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented May 10, 2018

This fixes : #46109.

@ithinuel ithinuel changed the title Add from and into bool for integer types Add From<bool> for integers and TryFrom<integers> for bool May 10, 2018

@@ -4259,6 +4259,25 @@ macro_rules! try_from_both_bounded {
)*}
}

macro_rules! try_bool_from {
($($source:ty),*) => {$(
#[unstable(feature = "try_bool_from", issue = "0")]

This comment has been minimized.

@ithinuel

ithinuel May 11, 2018

Author

Should I create an issue for this or is the PR number ok here ?

This comment has been minimized.

@ithinuel

ithinuel May 11, 2018

Author

I am not even sure this should be tagged as unstable.

This comment has been minimized.

@scottmcm

scottmcm May 12, 2018

Member

impls are insta-stable, so they should be marked stable with the corresponding release (currently 1.28, I think).

This comment has been minimized.

@ithinuel

ithinuel May 12, 2018

Author

Ok, Thank you.
So I changed :

  • the impl From<bool> for .. to stable 1.28.0 with the feature name lossless_bool_conv to be consistent with other lossless conversions.
  • impl TryFrom<T> for bool to the unstable feature try_from linking #33417 as the issue number.
@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented May 14, 2018

A practical use case of this can be found here : https://github.com/ithinuel/rusty-printer
you will need to build with the target thumbv7m-none-eabi.

cc @TimNN @clarcharr

@clarfon

This comment has been minimized.

Copy link
Contributor

clarfon commented May 14, 2018

I don't think that TryFrom impls should be added; int as bool doesn't work as-is and most people will interpret this as 0 => true, other => false like C and others do.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented May 14, 2018

int as bool doesn't work as-is and most people will interpret this as 0 => true, other => false like C and others do.

I am not sure to understand your point. At least in C, C++, JS and python True is 1 and False is 0.
Actually I don't know any language where True is 0.

int as bool does not compile indeed. rustc --explain E0054 explains that :

It is not allowed to cast to a bool. If you are trying to cast a numeric type to a bool, you can compare it with zero instead:

let x = 5;

// Not allowed, won't compile
let x_is_nonzero = x as bool;
let x = 5;

// Ok
let x_is_nonzero = x != 0;

This demonstrates 0 is False, any other value is True.

The implementation proposed here prevents loss by only accepting 1 as True.

@clarfon

This comment has been minimized.

Copy link
Contributor

clarfon commented May 14, 2018

Err, you're right, I swapped true and false there.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented May 20, 2018

@scottmcm @clarcharr @shepmaster
Do you have any additional comment ?
What is the next step ?

@scottmcm

This comment has been minimized.

Copy link
Member

scottmcm commented May 20, 2018

@ithinuel I'm torn. On the one hand I think bool: TryFrom<i128> is kinda weird. But at the same time, I like i128: From<bool>, and in general think that T: From<U> should always have a corresponding U: TryFrom<T>. So maybe there's nothing to do here but wait on libs to decide on it.

Well, you could remove the integer: From<bool> part of this, since that's FCP'd to go in as #50554. But if libs were to decide they don't want the TryFrom path that'd be wasted effort, so waiting is fine too.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented May 20, 2018

Could you elaborate why you find it weird ? here or on internals.

I could indeed remove the From part. I implemented it that way because I wasn't sure true is always 1 and false 0 but it is actually stated in the documentation : https://doc.rust-lang.org/std/primitive.bool.html

EDIT: I just noticed that the other PR is lacking tests.

clarfon added a commit to clarfon/rust that referenced this pull request May 20, 2018

@scottmcm

This comment has been minimized.

Copy link
Member

scottmcm commented May 20, 2018

@ithinuel Because there's two interpretations. The "bool is i1" interpretation says it should only accept 1 for true, but it's very common in languages to accept anything non-zero as true. That's well-known in C, but also things like .Net's Convert.ToBoolean do it.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented May 20, 2018

@scottmcm We could argue something equivalent to the conversion from u32 to u8 where the truncation is also well known in a lot of languages.

TBH I don't really mind either way, what really matters is to have TryFrom or even From implemented for booleans too. I opted for this implementation to make it consistent with the opposite operation that always gives 0 or 1 and not 0 or any value with at least 1 bit set.

@clarfon

This comment has been minimized.

Copy link
Contributor

clarfon commented May 20, 2018

TBQH I think that bool as int would have been better not implemented, as the lack of symmetry tends to trip up API decisions like this. But, because this is part of the language now, it may be better to just have From but not TryFrom here.

@shepmaster

This comment has been minimized.

Copy link
Member

shepmaster commented May 21, 2018

I'm going to go ahead and reassign this to @TimNN who has better context due to #50554.

r? @TimNN

@rust-highfive rust-highfive assigned TimNN and unassigned shepmaster May 21, 2018

@pietroalbini

This comment has been minimized.

Copy link
Member

pietroalbini commented May 28, 2018

Ping from triage @TimNN! This PR needs your review.

@TimNN

This comment has been minimized.

Copy link
Contributor

TimNN commented May 28, 2018

We have #50554 for the bool -> int direction, so let's focus this PR on the int -> bool direction, i.e. the proposed implementation of impl TryFrom<int> for bool.

The largest concern I see in the discussion above is that different people could have different expectations for the int -> bool conversion.

  1. Based on the convention 0 = success, * = error, one could argue that 0 -> Ok(true), * -> Ok(false).
  2. To be strictly in line with the bool -> int implementation from #50554, one could argue that 0 -> Ok(false), 1 -> Ok(true), * -> Err(()).
  3. Following the C compiler, one could argue that 0 -> Ok(false), * -> Ok(true).

Note that (1) and (3) could be implemented as From instead of TryFrom.

The main benefit of this PR is for generic & macro code, as I understand it.


To me it seems like the core question is whether bool should behave as a "tiny int" (i.e. an i1, or rather a u1) or as a completely distinct type.


Since this will need an FCP anyway, due to insta-stable implementations, I believe the best way forward is to delegate this to @rust-lang/libs, either to discuss how this should be implemented (1) - (3) or to propose a merge / close FCP.

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented May 28, 2018

different people could have different expectations

I think this is a reason to not have these conversions at all. There is precedent with as casts: (true as u32, false as u32) is (1, 0), but 0 as bool causes "error[E0054]: cannot cast as bool" / "unsupported cast"

@TimNN

This comment has been minimized.

Copy link
Contributor

TimNN commented Jun 1, 2018

@SimonSapin I don't think impl<T, U> TryConvert<T> for U where U: TryInto<T> {} and impl TryConvert<bool> for u32 can co-exist in the current coherence rules.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 1, 2018

This was my first attempt but unfortunately it is not possible as

= note: upstream crates may add new impl of trait `std::convert::From<u32>` for type `bool` in future versions

That's why the solution is to actually implement it in the upstream crate 😄

clarfon added a commit to clarfon/rust that referenced this pull request Jun 1, 2018

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Jun 2, 2018

☔️ The latest upstream changes (presumably #50554) made this pull request unmergeable. Please resolve the merge conflicts.

@ithinuel ithinuel force-pushed the ithinuel:add-from-and-into-bool-for-integer-types branch from 3f20ecd to 25a622a Jun 2, 2018

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 2, 2018

Squashed for a clean history & rebased :)

@ithinuel ithinuel changed the title Add From<bool> for integers and TryFrom<integers> for bool Add TryFrom<{integer}> for bool Jun 2, 2018

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 3, 2018

@sfackler I updated the example. It will work with the next nightly that will include #50554.
However, having a special case for bool is really "meh".

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 10, 2018

@rust-lang/libs any update on this PR ?

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Jun 10, 2018

I don't believe our feelings on these impls have changed.

If you want specific behavior for your application, you can make a private trait that does that. It just can't have a blanket impl.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 10, 2018

@sfackler Can you give an example of what you mean ?

Using a trait to extend the implementation does not solve the issue as demonstrated here.
The trait could be implemented for all primitive types but it would then require the users to implement From<_> as well as the new trait, which cannot be private, for their own types.

This example show a working example on my specific application.
It stays quite simple thanks to the :vis matcher.

@TimNN

This comment has been minimized.

Copy link
Contributor

TimNN commented Jun 10, 2018

@ithinuel: So, if you're main use case is macros, I've just thought about another solution: Add your own trait MyTryInto { try_into(...) -> ...; } and implement for types without an std::TryInto implementation. Then ensure that both, std::TryInto and MyTryInto are in scope. IIUC, the compiler should pick the correct trait as long as the implementations don't overlap.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 10, 2018

@TimNN unfortunatly, if both traits are in scope

   = note: candidate #1 is defined in an impl of the trait `core::convert::TryInto` for the type `_`
   = note: candidate #2 is defined in an impl of the trait `silica::register::MyTryInto` for the type `u32`

tested on nightly.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Jun 10, 2018

The trait could be implemented for all primitive types

This is what I mean.

@ithinuel

This comment has been minimized.

Copy link
Author

ithinuel commented Jun 11, 2018

The trait could be implemented for all primitive types

This is what I mean.

but it would then require the users to implement From<_> as well as the new trait, which cannot be private, for their own types.

This means code duplication or redundant boiler plate. 😐 This is not something we want to inflict to users.

@TimNN

This comment has been minimized.

Copy link
Contributor

TimNN commented Jun 12, 2018

@rust-lang/libs, it looks like the consensus here is to close the PR. Could one of you please do so if that is correct or initiate an @rfcbot fcp close

F001 added a commit to F001/rust that referenced this pull request Jun 24, 2018

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Jun 27, 2018

there's not necessarily an inherent truthiness value nor as conversion to back it up

I think this is a strong reason.

@rfcbot fcp close

@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Jun 27, 2018

Team member @SimonSapin has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Jun 27, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Jul 7, 2018

The final comment period, with a disposition to close, as per the review above, is now complete.

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Jul 7, 2018

As discussed we’re not taking this impl, but thanks for you work on this anyway @ithinuel.

@SimonSapin SimonSapin closed this Jul 7, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.