Skip to content
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

Exclamation ! return type not respected in unwrap_or_else #94418

Closed
plagerweij opened this issue Feb 27, 2022 · 4 comments
Closed

Exclamation ! return type not respected in unwrap_or_else #94418

plagerweij opened this issue Feb 27, 2022 · 4 comments
Labels
C-bug Category: This is a bug.

Comments

@plagerweij
Copy link

plagerweij commented Feb 27, 2022

I often write a function that logs an error in a custom way and then panics afterwards, e.g.

fn error<T: std::fmt::Display>(err: T) -> ! {
    // Do something with err

    panic!("{}", err);
}

fn main() {
    std::env::var("NONEXISTENT").unwrap_or_else(|err| error(err));
}

This has worked for me for a long time, but I recently noticed this clippy warning:

warning: redundant closure
 --> src/main.rs:8:49
  |
8 |     std::env::var("NONEXISTENT").unwrap_or_else(|err| error(err));
  |                                                 ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `error`
  |
  = note: `#[warn(clippy::redundant_closure)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

warning: `exclamation-error` (bin "exclamation-error") generated 1 warning

I'd like to catch all clippy warnings in CI pipelines and just adding #[allow(clippy::redundant_closure)] everywhere I call that function is suboptimal, but when I try to fix it as clippy suggests I get this compiler error instead:

error[E0271]: type mismatch resolving `<fn(VarError) -> ! {error::<VarError>} as FnOnce<(VarError,)>>::Output == String`
    --> src/main.rs:8:34
     |
8    |     std::env::var("NONEXISTENT").unwrap_or_else(error);
     |                                  ^^^^^^^^^^^^^^ expected struct `String`, found `!`
     |
     = note: expected struct `String`
                  found type `!`
note: required by a bound in `Result::<T, E>::unwrap_or_else`

For more information about this error, try `rustc --explain E0271`.
error: could not compile `exclamation-error` due to previous error

Consider me confused. Isn't the ! return type supposed to indicate that it's a diverging function? What makes this not work in a unwrap_or_else?

Meta

rustc --version --verbose:

rustc 1.59.0 (9d1b2106e 2022-02-23)
binary: rustc
commit-hash: 9d1b2106e23b1abd32fce1f17267604a5102f57a
commit-date: 2022-02-23
host: x86_64-unknown-freebsd
release: 1.59.0
LLVM version: 13.0.0

(found same behaviour on nightly)

@plagerweij plagerweij added the C-bug Category: This is a bug. label Feb 27, 2022
@plagerweij plagerweij changed the title ! return type not respected in unwrap_or_else Exclamation ! return type not respected in unwrap_or_else Feb 27, 2022
@wwylele
Copy link
Contributor

wwylele commented Feb 27, 2022

I think this is a bug in clippy rather than in rust. Rust cannot implicitly coerce a function even though the return type is coercable, and this isn't limited to ! type, or unwrap_or_else. For example

fn foo() -> Box<i32> {
    Box::new(42)
}

fn bar<F: FnOnce()->Box<dyn std::fmt::Debug>>(f: F) {
    println!("{:?}", f())
}

fn main() {
    bar(||foo()); // this works
    // bar(foo); // this doesn't work
}

And this code also trips clippy for the same warning. Clippy doesn't take the coercion at function return position into account, and provides a wrong suggestion

@wwylele
Copy link
Contributor

wwylele commented Feb 27, 2022

Clippy side issue: rust-lang/rust-clippy#7812

@plagerweij
Copy link
Author

plagerweij commented Feb 27, 2022

Ah, thanks for pointing that out. Closing this issue.

@wwylele while I have you, could you answer one last question (not clippy related)? When I remove the ! return type and still use my original closure I get the following error:

error[E0308]: mismatched types
 --> src/main.rs:8:55
  |
8 |     std::env::var("NONEXISTENT").unwrap_or_else(|err| error(err));
  |                                                       ^^^^^^^^^^ expected struct `std::string::String`, found `()`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `exclamation-error` due to previous error

So clearly ! is useful in this case. Why is it that ! seems to work for closures but not when I call my error function directly?

@WaffleLapkin
Copy link
Member

@plagerweij

! represents the type of computations which never resolve to any value at all
https://doc.rust-lang.org/std/primitive.never.html

! is useful because it can be coerced to any type (in this case to String) passing just error doesn't work because while ! can be coerced, the function that returns ! can't be coerced. So by introducing a closure you introduce a coercion site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

3 participants