`Result::ignore`
Proposal
Problem statement
Result is a #[must_use] type, but there is no idiomatic way to ignore it. Consider
fn try_foo() -> Result<A, E> { ... }
fn main() {
try_foo();
}
This will give you a warning, even if you don't care about the result. Now you have to discard it somehow.
Alternatives
One may try
let _ = try_foo();
but if we now change signature to
async fn try_foo() -> Result<A, E> { ... }
this will result in the Future silently being dropped without polling it even once. It doesn't help that Future is a #[must_use] type too.
You can try to combat this with
let _: Result<_, _> = try_foo();
but that's quite mouthful and is not something that will come to your mind right away.
The Rust community seems to have developed a trick for this situation, which is using .ok().
This works because this method (and the returned Option<T> type) is not marked #[must_use] for some reason (unlike is_ok()) and is super unintuitive when you read this for the first time.
Motivating examples or use cases
Quite a lot even in the rustc itself. Add #[must_use] to Result::ok and see the number of errors you get.
Solution sketch
- Add the following method to
Result:
fn ignore(self) {}
- Mark
.ok()&.err()as#[must_use]afterignoreis stabilized.
Maybe name it ignore_result() instead, for clarity.
Links
Example of the problem with unpolled Futures (and general discussion of the situation): https://users.rust-lang.org/t/what-is-the-best-way-to-ignore-a-result/55187
A "what is your favorite way to ignore a Result?" poll I've randomly stumbled upon today: https://t.me/psauxww/1259 . Would not exist if there was an idiomatic way to do it or if a Rust programmer did not need to ignore a Result every once in a while.
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
this will result in the Future silently being dropped without polling it even once.
Why not let _ = foo().await;?
Why not
let _ = foo().await;?
I think the point is that if the API evolves (goes from fn() -> Result<A, B> to async fn() -> Result<A, B>) then the previous usage of let _ will continue to compile but now mean something different: ignoring the future instead of ignoring the Result.
Why not
let _ = foo().await;?I think the point is that if the API evolves (goes from
fn() -> Result<A, B>toasync fn() -> Result<A, B>) then the previous usage oflet _will continue to compile but now mean something different: ignoring the future instead of ignoring theResult.
Precisely. let _ = does not get along well with any type changes to the return value. It's just that the Future is the most egregious example, because accidentally dropping it significantly changes the control flow.
Oops, wrong button
You would still have a problem if the function changed from Result<T, Infallible> to a real error you should handle.
There are also many other #[must_use] types and functions that should be considered. I don't have any more general ideas, apart from explicit let _: Type as you mention, but I'm sure we wouldn't want to add ignore everywhere.
You would still have a problem if the function changed from
Result<T, Infallible>to a real error you should handle.
Won't there be some special unwarp for Result<_, !> once ! geta stabilized?
Also, this is still a problem with current status quo of .ok().
Finally, this ACP is partially about adding #[must_use] to .ok() and .err(), which is blocked on having no idiomatic way to ignore a Result.
I'm really sympathetic to this one, because I managed to write let _ = func(); once when I meant to write let _ = func().await;, resulting in a hard-to-track-down bug.
We discussed this in the @rust-lang/libs-api meeting. Considering we have dedicated language syntax for ignoring results (_ = ) and that we now have a clippy lint against futures that are ignored, we don't feel that a new method is justified.