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
Contributor

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

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.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 10, 2018
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

@ithinuel ithinuel May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😮 I'm few hours late

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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 use from here but I can change to TryFrom if required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use TryFrom instead.

@ithinuel
Copy link
Contributor Author

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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 am not even sure this should be tagged as unstable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Tracking issue for TryFrom/TryInto traits #33417 as the issue number.

@ithinuel
Copy link
Contributor Author

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

@clarfonthey
Copy link
Contributor

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
Copy link
Contributor 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.

@clarfonthey
Copy link
Contributor

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

@ithinuel
Copy link
Contributor Author

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

@scottmcm
Copy link
Member

@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
Copy link
Contributor 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.

@scottmcm
Copy link
Member

@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
Copy link
Contributor Author

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

@clarfonthey
Copy link
Contributor

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

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

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

@TimNN
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 Add From<bool> for int types #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.

@TimNN TimNN added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 28, 2018
@SimonSapin
Copy link
Contributor

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"

@SimonSapin
Copy link
Contributor

I think @sfackler may have been suggesting to use a private trait. Something like:

trait TryConvert<T> {
    fn try_convert(self) -> Result<T, ()>;
}

impl<T, U> TryConvert<T> for U where U: TryInto<T> {
    fn try_convert(self) -> Result<T, ()> { self.try_into().map_err(|_| ()) }
}

impl TryConvert<bool> for u32 {
    // ...
}

@TimNN
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
Copy link
Contributor 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 😄

clarfonthey pushed a commit to clarfonthey/rust that referenced this pull request Jun 1, 2018
@bors
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 add-from-and-into-bool-for-integer-types branch from 3f20ecd to 25a622a Compare June 2, 2018 17:20
@ithinuel
Copy link
Contributor 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
Copy link
Contributor 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
Copy link
Contributor Author

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

@sfackler
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
Copy link
Contributor Author

@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
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
Copy link
Contributor Author

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

The trait could be implemented for all primitive types

This is what I mean.

@ithinuel
Copy link
Contributor 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
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

@SimonSapin
Copy link
Contributor

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
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 rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. labels Jun 27, 2018
@rfcbot
Copy link

rfcbot commented Jun 27, 2018

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

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Jun 27, 2018
@rfcbot
Copy link

rfcbot commented Jul 7, 2018

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

@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Jul 7, 2018
@SimonSapin
Copy link
Contributor

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
Labels
disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. finished-final-comment-period The final comment period is finished for this PR / Issue. S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.