Why rust allows to ignore Result as a return?

It seems rather silly that ignoring Result isn't flagged as a no-op and compilation isn't stopped.

There's a built in lint for this, so you'll at least get a warning by default. I do recommend treating warnings as errors at least in your CI or release testing process, if not sooner.

You can use #[deny(warnings)] in code or RUSTFLAGS="-D warnings" in the environment to turn all warnings into errors, or you can deny specific lints.

In general, some things are lints instead of hard errors because it might be useful to allow them temporarily, during development, while still ensuring they get fixed before the code ships.

5 Likes

I agree. That's why my main.rs or lib.rs has #![deny(unused_must_use)] at the top. There are a few places where I don't want to return the Result, such as in the closure run by a thread. In those places I eat the error with

let _ = thing_returning_result().map_err(|e| handle_error(e));

3 Likes

Yes, I agree. That's why in my opinion during the development process not dealing with Result as a return could be allowed but marked as a warning. In the release build, this should not compile.

Your lib.rs or main.rs will have that. But there will be thousands others that won't have that. Leaving things in hands of programmers (like C/C++ did) proved to be a bad choice. You cannot depend on people that they will follow only best practices. But compiler has the power to enforce that.

It's a warning. There are lots of things that you wont catch if you ignore the warnings.

8 Likes

I know it is a warning. But I believe that in release should be an error, something akin enforcing correct practices and not letting programmer to skip them if they don't feel like.

For better or worse, turning it into an error by default (outside of an edition bump) would break code that currently works. If you want to advocate for that change, the internals forum is a more appropriate venue. That's where discussions about the language design happen; this forum is for helping people use the current version of Rust.

1 Like

Ignoring the result of an operation is not necessarily an error or bug or a mistake. We already have warnings for it. I think what compiler must stop us from doing is using the result of operation without checking the validity of it, and Rust already does that thanks to pattern matching and enums.

1 Like

Currently what counts as "used" is a heuristic. If it was changed to be compile-breaking, then it'd have to be specified exactly, and never improved.

3 Likes

And how would you improve it?

#[must_use] is a heuristic, not a linear type, so there are cases where it doesn't do anything, and what "used" means is arbitrary.

For example, this compiles without warnings, even though the result is never checked:

use std::fs::File;

fn main() {
    let x = (File::open(""), 1);
    println!("{}", x.1);
}
13 Likes

And do you really think that not having that program compiled in release mode, exactly for the reason I mentioned is preferable?
Wouldn't you really prefer to have the compiler tell you that you doing something that at best makes no sense, at worse is simply dangerous?

It already tells that as a warning.

What do you mean by dangerous? In the worst scenario it is a bug, and it doesn't makes sense to me that by default compiler should assume it is a logical bug and stop the compilation based on that assumption.

1 Like

Which you can freely ignore.

Yes, but if compiler would refuse to compile it that bug wouldn't be there. Surely this is preferred way?

Just to understand, are you actually proposing that code that compiles without --release would not compile with the flag?

I don't think that would be what people generally want. The code should compile the same, do the same (except for faster) with --release. For two reasons:

  • It makes sense to have only cargo build (or test, but without --release) in your per-commit CI. But then, it would pass and actual release wouldn't.
  • I often end up having to run „experimental“ versions/code with --release because I have just too many data to feed it to be usable without it. If you accept that during development it makes sense to allow that kind of flexibility (to ignore results), then even though I'm compiling with --release, this is under development.
10 Likes

But this is already not true in the current Rust.

If you're referring to panic-on-overflow, then yes, you're right. That's not a good argument to make it worse, though. And arguably, here the direction of what's allowed is in the other direction (it's more strict in debug build).

2 Likes

In the worst case it might be a bug. In many cases ignoring an unused Result is benign. For instance, write! to a &mut String returns a Result, but it will always be Ok – writing to a String will never raise a fmt::Error. The compiler doesn't know the difference and will warn anyway.

Note that in most cases where a function returns Result, the caller needs to use the Ok part anyway, which forces them to deal with the Err case before they can continue with the caller's logic. File::open is an example: it would be really hard to accidentally forget to use the result because the next thing you're going to want to do after opening a file is read or write to it, and to do that you need to handle the Err case in some way. It's not like in certain other languages where you can forget to check whether the call succeeded and still proceed to use the return value as if it had.

When I run into unused_must_use it's most often because I'm using a function that returns a Result, but I'm using it for side effects so I don't care about the successful return value. In such cases, it's not unlikely that I also don't care about the unsuccessful return value, either because it can't happen or it doesn't matter if it does. In fact, write! to a String is the only case I can think of that I've actually encountered unused_must_use, and in that case it's actually wrong. Elevating it to an error wouldn't make it better. (That doesn't mean I ignore the warning completely; if it's code that'll be committed, I'll fix it to suppress the warning first, it's just not a priority.)

The rustc dev guide has a section on diagnostic levels and lint levels which outlines the differences between errors and warnings. The rustc book also has a section on lint levels you would probably do well to read.

5 Likes