I’m for and_then
then. It feels more familiar and rustish.
Note that and_then
takes a closure that returns Option
so our example case would look like
(denominator != 0.0).and_then(|| Some(numerator / denominator))
This is no less verbose than to_option().map
and somewhat confusing if you consider this method by itself.
True. There may just be no useful and consistent API to add here.
I’ve forgotten possibly the least intrusive variation: implementing Into<Option<()>> for bool
which would give us Option::from(expr)
, so the example becomes
Option::from(denominator != 0.0).map(|_| numerator / denominator)
That’s rather unintuitive. I’d expect Option::from(bool)
to yield Some(bool)
:
impl<T> Into<Option<T>> for T {
fn into(self) -> Option<T> {
Some(T)
}
}
Oh, right. Option::from
might just be ambiguous.
Why would Option::from(x)
equal Some(x)
when you can just Some(x)
?
For the same reason I proposed [Pre-RFC] From<T> for Result<T, E>. I’m not sure we should add that impl, but it’s a reasonable impl.
I think that the ‘usual’ method names of Option would only confuse people. I don’t want us to build clever little footguns to trip up other programmers.
If we really want one, the method on bool should have a completely different name than any Option method, as to minimize confusion. On the other hand, the name should be short and meaningful.
My .02$:
_.and_some(_)
_.as_opt(_)
_.some(_)
_.then(_)
_.opt(_)
Currently, then
would be my favourite name. Similar enough to and_then
, yet different enough not to be confused.
By the way, how strong does negation bind? I’d hate to see someone complaining because !b.then(x)
didn’t work as expected…
Edit: method application binds more strongly than bool negation: http://is.gd/QHpi1c – yes, removing parenthesis just results in a (somewhat understandable) compiler error. Still, this is a footgun.
Wow this yielded quite some discussion.
I have come to the conclusion that if x { Some(y) } else { None }
is way more readable than any of the proposed closure functions. It should be made totally clear what’s going on. Short circuiting boolean operators is definitely not the way to go. Using booleans as anything but booleans breaks the code-reading flow. So here’s a macro that’s not worse than the closure solutions imo. Not saying it’s better though.
macro_rules! opt (
($cond:expr => $some:expr) => {
if $cond { Some($some) } else { None }
}
);
fn test(cond: bool) -> Option<i32> {
opt!(cond => 5)
}
You could also simply define
fn opt<T>(b: bool, t: T) -> Option<T> {
if b { Some(t) } else { None }
}
and use with
opt(cond, 5)
Edit: ker is right, this is only the same if the calculation of the second argument doesn’t fail.
that won’t work for t
s that can fail like numerator/denominator
when denominator
is zero
For anyone still interested in this, there’s now the boolinator crate.
[quote=“ker, post:17, topic:1729”]type bool = Option<()>; const false: bool = Some(()); const true: bool = None;
[/quote]
I’m irrationally fascinated by this, but think we can do better now that we have another 18 months of language progress:
type bool = Result<(),()>;
const false: bool = Err(());
const true: bool = Ok(());
fn careful_div(a: i32, b: i32) -> Result<i32,()> {
(b != 0)?;
a / b
}
(Aside: does <(),()>
look like an owl to anyone else?)
More seriously, if A { B }
currently means if A { B } else { () }
, right? If we wanted to do this, it feels like it might be best done in the language, like letting loop
return non-unit was.
if A { B }
could (ignoring back-compat) be changed to mean if A { Some(B) } else { None }
. Existing cases would be Some(())
(that looks familiar…) instead of ()
, but that doesn’t make me sad. (This is, of course, exactly what ker’s macro does. I agree that none of the closure syntaxes were great.)
And then the function in the OP would be
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator != 0.0 {
numerator / denominator
}
}
That’s pretty elegant, actually…
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.