Add None and Err coercion operator
Basically it will be like unwrap_or()
I really like in kotlin the elvis operator: ?:
It allows you to write code like this:
let string = optional_string ?: "";
Add None and Err coercion operator
Basically it will be like unwrap_or()
I really like in kotlin the elvis operator: ?:
It allows you to write code like this:
let string = optional_string ?: "";
Kotlin's ?:
operator is more similar to .unwrap_or_else(|| ...)
. Except that it also allows control flow on the right, e.g.
val string = optional_string ?: throw SomeError()
// or
val string = optional_string ?: return 0
// or
val string = optional_string ?: continue
Rust has let-else
for this:
let Some(string) = optional_string else {
return 0
};
I really like how concise and powerful ?:
is. JavaScript's ??
operator is similar, but does not support control flow keywords.
However, it will be hard to justify an elvis operator in Rust, because unwrap_or_else
and let-else
already exist, so there isn't a strong incentive to add another syntax, which will make Rust (which is already a complex language) even more complex.
I gues let
-else
is basically the same thing and so is unwrap_or_else()
but ?:
is really concise and faster write
I doubt we'll get an operator for Option
only, so one should consider how this could work with Try
types (or perhaps with all types). But the naive desugaring for Try
types...
// expr ?: ...
// becomes
match (expr).branch() {
ControlFlow::Continue(happy) => happy,
ControlFlow::Break(_) => ...,
};
let string = fallible_string ?: ""; // Error silently discarded :-(
...results in throwing out the Residual
-- for example, throwing out the Err
variant of a Result
-- which is not something that should be encouraged by being easier to type.[1]
So if this idea goes anywhere, there should be a desugaring that "catches" the Residual
.
The existing Residual
s of Try
types are enum
s with some variants omitted (by filling them in with Infallible
/!
). This plays nicely in combination with exhaustive_patterns
when there's only one variant left.
Consider a desugaring like
// expr ?: match [ident] { ... }
// becomes
match (expr).branch() {
ControlFlow::Continue(happy) => happy,
ControlFlow::Break(residual) => match [ident @] residual {
...
// (residual is not nameable except via ident)
}
}
let string = optional_string ?: match { None => "" };
let string = fallible_string ?: match { Err(e) => {
eprintln!("Wha? '{e}' Continuing on anyway...");
""
}};
Still pretty clunky. But as a special case, instead of match
you can supply a single pattern (which would result in an error if the pattern doesn't cover all inhibited possibilities):
// expr ?: pattern => ...
// becomes
match (expr).branch() {
ControlFlow::Continue(happy) => happy,
ControlFlow::Break( pattern ) => ...
// Same thing effectively:
/*
ControlFlow::Break(residual) => match residual {
pattern => ...
}
*/
}
let string = optional_string ?: None => "";
let string = fallible_string ?: Err(e) => {
eprintln!("Wha? '{e}' Continuing on anyway...");
""
};
Silent discards are still too easy, but perhaps this could be a lint.
let string = fallible_string ?: _ => "";
Why not add a "type Break
" to Try
or such instead?
Residual
has to have only have one possibility.enum Example<T, U, V> { Happy(T), Sad(U), Eh(V) }
// ...
type Residual = Example<Infallible, U, V>
Err(e)
instead of just having e
(for example) communicates that you're handling an error much betterlet else
also has this weakness to some extent, but it's much easier to spot a let Ok(_) = ... else
in review because the Ok
must be spelled out (and the must-diverge rule knocks out some temptations too) ↩︎
I think it would be better if had another trait called TryOr
trait TryOr {
type OkType;
fn try_or(self, or: Self::OkType) -> Self::OkType;
}
impl<T, E> TryOr for Result<T, E> {
type OkType = T;
#[inline]
fn try_or(self, or: Self::OkType) -> Self::OkType {
match self {
Ok(ok) => ok,
Err(_) => or,
}
}
}
result ?: …
=> result.try_or(…)
Here's an old related thread: Something for coalescing; aka generalized/improved `or_else`
That still just throws away the error value.
but the point is to get rid of the error
The ?:
was also discussed in this thread: Elvis operator for Rust
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.