-
Notifications
You must be signed in to change notification settings - Fork 20
Add Option::try_or_else
#59
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
Comments
Can you also describe the difference with |
Anyway, I've wanted this many times, and usually it takes staring at the list of methods on option for a while first to make sure that what I want isn't there (it's not). I don't really like the name |
how about impl<T> Option<T> {
pub fn try_or_else<F: FnOnce() -> R, R>(self, f: F) -> R
where
R: Try<Output = T>,
{
try {
if let Some(v) = self {
v
} else {
f()?
}
}
}
} |
I'm not personally attached to it. Another option I see is to break the
I am not too familiar with the |
There's That's an interesting idea if we made |
It doesn't really need a if let Some(v) = self {
Try::from_output(v)
} else {
f()
} |
@cuviper Awesome suggestion, it seems to work really well: If no one has any objections or other opinions I'll edit the original proposal to do this instead. (I wonder...since this is obsoleting |
The new Cargo docs now call that Possibly-breaking: introducing a new function type parameter. But |
Here's a break if we try to change
That could have used |
A possibly-harebrained idea is to put this on fn try_or_else<F, R>(self, f: F) -> R
where
F: FnOnce(Self::Residual) -> R,
R: Try<Output = Self::Output>,
{
match self.branch() {
ControlFlow::Continue(c) => R::from_output(c),
ControlFlow::Break(b) => f(b),
}
} But you would have to |
🆕 OK I've updated this proposal to be
(Somewhat tangential to this, but out of curiosity is making that change to drop |
Maybe for high-profile stuff, but for the most part the standard library is edition-neutral. There's not an easy mechanism for that yet, so we end up tinkering in the compiler, which is not great. The primary example is |
That idea would also be fine on impl<T, E> Result<T, E> {
pub fn try_or_else<F, R>(self, f: F) -> R
where
F: FnOnce(E) -> R,
R: ops::Try<Output = T>,
{
match self {
Ok(t) => R::from_output(t),
Err(e) => f(e),
}
}
} |
I see the value in the symmetry of also replacing/generalizing |
The name |
Do you have any alternative naming suggestions? I suspect everyone here would probably agree that it would all be nicer if this had just been what |
This can already be done using let v: String = std::env::args()
.nth(1)
.map_or_else(|| std::fs::read_to_string("/etc/someconfig.conf"), Ok)? At this point we have a pretty high bar for adding new methods to |
OK, fair. It's not the most intuitive construct; the dangling How about this patch to the std docs which highlights this case more?
|
note |
Thanks, fixed! |
The doc change looks fine to me. |
Moving this from rust-lang/libs-team#59 where an API addition was rejected. But I think it's valuable to add this example to the documentation at least.
…result, r=Mark-Simulacrum Option::map_or_else: Show an example of integrating with Result Moving this from rust-lang/libs-team#59 where an API addition was rejected. But I think it's valuable to add this example to the documentation at least.
…result, r=Mark-Simulacrum Option::map_or_else: Show an example of integrating with Result Moving this from rust-lang/libs-team#59 where an API addition was rejected. But I think it's valuable to add this example to the documentation at least.
Moving this from rust-lang/libs-team#59 where an API addition was rejected. But I think it's valuable to add this example to the documentation at least.
…=Mark-Simulacrum Option::map_or_else: Show an example of integrating with Result Moving this from rust-lang/libs-team#59 where an API addition was rejected. But I think it's valuable to add this example to the documentation at least.
Proposal
Add
Option::try_or_else
.(note: This was formerly proposed as
Option::result_or_else
that only handledResult
)Problem statement
This generalizes
Option::or_else
to also handleResult
, which is very useful in cases where we want to convert anOption<T>
to aT
, but use a fallible (Result<T>
) fallback path in a combinator chain.Motivation, use-cases
This could instead be:
This convenience method is particularly useful when chaining further combinators, e.g.:
Solution sketches
Links and related work
I discovered/made up this idiom of using
map(Ok).unwrap_or_else()
while working on a project, and it made some code much more elegant versus what I was doing before with multiple-linematch
statements.I then did some looking around via code search engines:
And there's quite a lot of usage of this. Enough, I think that there's an argument for promoting this pattern to std.
I was chatting with @cuviper about this and he mentioned:
"another way to write this is
option.ok_or(()).or_else(|()| new_result)?
"I did some further searching, and there's also some code using this variant, though less:
Notably the first link shows a hit in cargo.
Later, several people pointed out in discussion that by using the
Try
trait, we can effectively generalize the currentOption::or_else
to handle bothOption
andResult
for the callback.What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.
The text was updated successfully, but these errors were encountered: