-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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 methods for converting from bool
to Option<T>
#2757
Conversation
Adding the language team since the actual implementation of this requires the addition of a language item |
@varkor I think it would also be good to add a few examples of before/after for real world use-cases, e.g. in the compiler or elsewhere, that would have benefited from this RFC. I think it would also be worth noting that this encourages the use of more closures. This ostensibly has the drawback of longer compile-times. On the flip-side, it also is a good idea due to the restrictions imposed on imperative control flow ( |
There's now a reference implementation at rust-lang/rust#64255. |
I remember previous discussions on similar topics have regularly brought up the question of symmetry; aka "what about when |
@fbstj https://doc.rust-lang.org/nightly/std/ops/trait.Not.html -- we could add it to the prelude if necessary. I think |
Very often you can rewrite a condition to avoid negation, and if that's not convenient (if the condition is used elsewhere, for instance), negation with |
And on the off chance we do, we are free to add more methods later, so this feels very safe. ;) |
Add methods for converting `bool` to `Option<T>` This provides a reference implementation for rust-lang/rfcs#2757.
I think the existing methods that are closest to the ones being proposed here are: x.ok(); // Result<T, E> → Option<T>
x.err(); // Result<T, E> → Option<E>
x.ok_or(y); // Option<T> → Result<T, E>
x.ok_or_else(closure); // Option<T> → Result<T, E>
x.and(y); x.and_then(closure); // Result<T, E> → Result<U, E>
x.or(y); x.or_else(closure); // Result<T, E> → Result<T, F>
x.and(y); x.and_then(closure); // Option<T> → Option<U>
x.or(y); x.or_else(closure); // Option<T> → Option<T> I've noticed that the first four methods, which transform between different "monad kinds", explicitly mention the variant to transform to. The rest (and/and_then/or/or_else), on the other hand, stays within Option and Result respectively. Based on this precedent, I think that methods which convert from |
Unfortunately, they're not that consistent. Both the methods from |
Seeing the name debate, I just want to ask why not |
Sorry, I skimmed through the discussion and didn't see it, but has something along the the following lines been considered? I realize that this goes back to putting the method on the Option rather than the bool and I didn't see if that had been ruled out entirely as unworkable in the earlier RFC.
I don't know if there's a general rule against using Presumably a lazy version would be a bit verbose though, which might make it a non-starter
|
I'm not sure why this is tagged T-lang; this is entirely a libs decision. |
Ah, just saw @Centril's comment (which was marked as resolved). For the sake of simplicity, I'd suggest treating the questions orthogonally; can we just ask if anyone on @rust-lang/lang objects to the creation of this lang item for |
[boolinator](https://docs.rs/boolinator/2.4.0/boolinator/)), but this functionality is commonly | ||
desired and an obvious candidate for the standard library, where an external crate for such simple | ||
functionality is not convenient. | ||
|
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.
Might as well mention if some_condition { t }
as an alternative.
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.
Inlining a method body is always a possible alternative, so mentioning it specifically here isn't useful.
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.
@varkor that does not mean inlining the method body, but to make if x { y }
evaluates to Some(y)
if x
is true and None
if x
is false (basically an even more controversial version of https://internals.rust-lang.org/t/pre-rfc-break-with-value-in-for-while-loops/11208)
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. Unless there are serious proponents of that approach, I don't think it's worth including, as it seems like an unviable alternative to me.
@dtolnay I believe @varkor addressed your concerns, so ping. :)
Well I agree that this lang item in particular is not a big deal (because as @SimonSapin notes, we already have a bunch of those for primitive types, and so in terms of spec complexity there's nothing novel here). However, I think it's important that institutional hindrances exist against the addition of lang items, and the language team is a good check on that. That said, as for adding a non-method, I didn't make a point about teams -- just that I personally didn't think it was worth it -- and it seems like there's agreement on that, so that's great. :) |
@rfcbot fcp cancel |
@dtolnay proposal cancelled. |
Proposing FCP to introduce unstable methods on bool for conditionally creating an Option::Some or running a closure. This RFC has been unusual compared to other libs RFCs in that whether we accept these methods hinges on whether we can settle on sufficiently clear names for the behavior; it isn't like e.g. MaybeUninit where we know we need the functionality and we'll pick a name one way or another. As such, mostly the FCP is to confirm that we are interested in gathering experience using this set of names—if it seems to work out then we'll follow up in the tracking issue with a separate FCP to stabilize later. impl bool {
fn then_some<T>(self, t: T) -> Option<T> {
if self {
Some(t)
} else {
None
}
}
fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
if self {
Some(f())
} else {
None
}
}
} This is the same functionality as from the earlier FCP in #2757 (comment), but new names as motivated in #2757 (comment). @rfcbot fcp merge |
Team member @dtolnay has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns: Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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 concern :(( Registering withoutboats's concern from #2757 (comment). |
@rfcbot resolve :(( |
Just to note, currently, I’m using the try-guard crate to do exactly that. I was happy to see that coming to
Yes, I agree with that statement. However, as you said it, both styles are useful. I’m a Haskeller and use lots of combinators, but I also use lots of imperative statements. It’s not a “pick one; stick to it” decision. Especially, I use After reading the pro and cons arguments from this thread, I’m not sure what the best decision should be (I’m not saying that I’m against the decision of closing, I just haven’t made up my mind about how I feel about it). After all, we can still have those combinators in lib crates. The best comparison to me is, again, Haskell, because I know it a lot, but Haskell has that kind of functions in base but the Haskell ecosystem is also using a lot libraries such as |
Landing unstable methods has already happened in rust-lang/rust#64255 (tracked in rust-lang/rust#64260) so I’m not sure what this FCP is for. Deciding on names (or removal) is basically the only thing left to do. |
As a datapoint, this feature turns out pretty useful in proc macros, and IMHO the current naming reads nicely: ruma/ruma-api@cb00188 |
Another use that would benefit from this is iterators (especially in long chains): let x = iter.filter_map(|(foo, condition)| condition.then(foo)); The alternatives include: let x = iter.filter_map(|(foo, condition)| {
if condition {
Some(foo)
} else {
None
}
); and: let x = iter
.filter(|(_, condition)| condition)
.map(|(foo, _)| foo) |
The most recent nightly still has |
The renaming PR has been sent to the bors queue. :) |
Rename `bool::then_*` to `bool::to_option_*` and use where appropriate Name change following rust-lang/rfcs#2757. Also try it out throughout the compiler in places I think makes the code more readable.
...and @bors has merged the renaming PR. :) |
Looks good to me. :) Thanks @varkor! We can follow up with an FCP to stabilize in rust-lang/rust#64260 after a few releases once we have some real world use cases outside of rustc to look at. |
@rfcbot cancel |
I tried using these combinators for some code that looked like this: let prev = if path.exists() {
ThinLTOImportMaps::load_from_file(&path).ok()
} else {
None
}; Unfortunately, since the closure fed to for let prev = path.exists().then(|| ThinLTOImportMaps::load_from_file(&path).ok()).flatten(); and at that point I decided that was obscure enough that it wasn't worth trying to adopt these combinators. Given that the signatures of That is, I'm suggesting this API: impl bool {
pub fn then_some<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
if self { Some(f()) } else { None }
}
pub fn then<T, F: FnOnce() -> Option<T>>(self, f: F) -> Option<T> {
if self { f() } else { None }
}
} If people want to keep a closure-less method in the API, I suggest pub fn and_some<T>(self, t: T) -> Option<T> {
if self { Some(t) } else { None }
} |
(oh, whoops; I guess the current conversation regarding the names is in rust-lang/rust#64260 ) |
For anyone coming from a search engine, |
Actually, bool::then() became stable in 1.50: |
Rendered.
The following pattern is prevalent in typical Rust code.
Here, I propose new methods for converting a
bool
to anOption<T>
, abstracting this pattern, to aid in readability and reducing repetition.This supersedes #2180, which was closed due to concerns about the suggested names and resolves #2606.