Rust: Error "cannot infer type" when using '?' in async block + bad diagnostic

Created on 12 Aug 2019  路  9Comments  路  Source: rust-lang/rust

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.

A-async-await A-diagnostics A-inference A-suggestion-diagnostics AsyncAwait-Triaged C-bug D-newcomer-roadblock F-async_await T-compiler

Most helpful comment

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>(())
    };
}

All 9 comments

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)

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>(())
    };
}

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

  • This isn't (necessarily) an inference failure: the original code snippet is somewhat underspecified, in that the return type of the async block can be any error type E that the Error value can be Into'd into.
  • What's needed is probably (a) a syntax to conveniently specify the error type and (b) a diagnostic that suggests said syntax.

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.

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.

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

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

@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.

You're absolutely right. Need more coffee.

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

Was this page helpful?
0 / 5 - 0 ratings