rust-lang / libs-team

The home of the library team
Apache License 2.0
110 stars 18 forks source link

`Result::ignore` #380

Open GrigorenkoPV opened 1 month ago

GrigorenkoPV commented 1 month ago

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

  1. Add the following method to Result:
    fn ignore(self) {}
  2. Mark .ok() & .err() as #[must_use] after ignore is 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

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):

Second, if there's a concrete solution:

the8472 commented 1 month ago

this will result in the Future silently being dropped without polling it even once.

Why not let _ = foo().await;?

shepmaster commented 1 month ago

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.

GrigorenkoPV commented 1 month ago

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.

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.

GrigorenkoPV commented 1 month ago

Oops, wrong button

cuviper commented 1 month ago

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.

GrigorenkoPV commented 1 month ago

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.

shepmaster commented 1 month ago

Won't there be some special unwarp for Result<_, !> once ! geta stabilized?

Result::into_ok