rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.28k stars 12.58k forks source link

Error "cannot infer type" when using '?' in async block + bad diagnostic #63502

Open russelltg opened 5 years ago

russelltg commented 5 years ago

This seems related to #42424, except I don't see an obvious workaround like in #42424.

#![feature(async_await)]

use std::io::Error;

fn make_unit() -> Result<(), Error> { 
    Ok(())
}

fn main() {
    let fut = async {
        make_unit()?;

        Ok(())
    };
}

Fails with the error

error[E0282]: type annotations needed for `impl std::future::Future`
  --> src/main.rs:11:9
   |
10 |     let fut = async {
   |         --- consider giving `fut` the explicit type `impl std::future::Future`, with the type parameters specified
11 |         make_unit()?;
   |         ^^^^^^^^^^^^ cannot infer type

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=ea7e5f7b6e1637f6a39aee0b8209c99e

First of all, it seems like this shouldn't be an error--it should be able to deduce that the return type is Result<(), io::Error>, but also the diagnostic does not work. If you try to add impl std::future::Future<Output=Result<(), Error>> as a type annotation, it also fails to compile because impl Trait is only allowed as return types and argument types.

Playground link

estebank commented 5 years ago

Opened https://github.com/rust-lang/rust/issues/63504 for the incorrect suggestion. For the main issue, ? interacts in non-obvious ways with type inference because it does implicit conversion. In this case, because fut doesn't explicitly constraint what the expected type is, rustc doesn't know what Result<(), std::io:Error> should be converted to.

There are other similar tickets filed about these kind of type inference issues: https://github.com/rust-lang/rust/issues/49391, https://github.com/rust-lang/rust/issues/38508, https://github.com/rust-lang/rust/issues/46333, https://github.com/rust-lang/rust/issues/46680, https://github.com/rust-lang/rust/issues/48089, https://github.com/rust-lang/rust/issues/61152, https://github.com/rust-lang/rust/issues/58517 and https://github.com/rust-lang/rust/issues/63082.

(larger work around ? tracked in https://github.com/rust-lang/rust/issues/31436)

cramertj commented 5 years ago

I don't see an obvious workaround

The workaround is this:

use std::io::Error;

fn make_unit() -> Result<(), Error> { 
    Ok(())
}

fn main() {
    let fut = async {
        make_unit()?;

        Ok::<(), Error>(())
    };
}
nikomatsakis commented 5 years ago

Discussing as part of a "triage effort". A few notes:

We lack a syntax. So maybe we can give a better diagnostic that at least hints at the solution. Or maybe an extended error code description.

estebank commented 5 years ago

For this case, we probably could suggest a temporary local binding with an explicit type:

    let fut = async {
        let x: Result<_, Error> = make_unit();
        x?;
        Ok(())
    };

As for the syntax, we could borrow the closure syntax and allow something like async -> Result<(), Error> {} or async::<Result<(), Error> {}, but those are uuuugly.

CC https://github.com/rust-lang/rust/issues/62570, as it is not the same error, but it is similar enough code that these two errors will likely happen in short temporal distance of each other.

cramertj commented 5 years ago

@estebank did you mean to include the ? after the x in your example above?

estebank commented 5 years ago

Yes, but was missing the final expr Ok(()) to be correctly equivalent the original code. Just updated it.

cramertj commented 5 years ago

@estebank That code still would not compile-- the error type annotation doesn't help there because it gets .into()'d in the ? usage. To make it work, you need to annotate the type of the final Ok value.

estebank commented 5 years ago

You're absolutely right. Need more coffee.

programmerjake commented 5 years ago

doesn't ? still use From rather than Into, or did I miss some happy RFC?

Dylan-DPC commented 1 year ago

Current output:

error[[E0282]](https://doc.rust-lang.org/nightly/error_codes/E0282.html): type annotations needed
  --> src/main.rs:11:9
   |
11 |         Ok(())
   |         ^^ cannot infer type of the type parameter `E` declared on the enum `Result`
   |
help: consider specifying the generic arguments
   |
11 |         Ok::<(), E>(())
   |           +++++++++
estebank commented 1 year ago

The only thing left is for inference to detect that E could have been Error and suggest that.