Rust: suggest `await` for future-related type errors

Created on 23 May 2019  路  22Comments  路  Source: rust-lang/rust

A common problem when using async-await is to forget to invoke .await. This often shows up as a type error, e.g. in a case like this (playground):

#![feature(async_await)]

async fn make_u32() -> u32 {
    22
}

fn take_u32(x: u32) {

}

async fn foo() {
    let x = make_u32();
    take_u32(x);
}

fn main() { }

currently we give a rather generic error:

error[E0308]: mismatched types
  --> src/main.rs:13:14
   |
13 |     take_u32(x);
   |              ^ expected u32, found opaque type
   |
   = note: expected type `u32`
              found type `impl std::future::Future`

it'd be nice if we could suggest adding .await somewhere.


A-async-await A-diagnostics AsyncAwait-Triaged C-enhancement F-async_await T-compiler

Most helpful comment

@tmandry yes, in slow progress since time is tough these days.

All 22 comments

cc @rust-lang/wg-diagnostics @estebank

@wycats was reporting a much worse error, but I'm quite sure what he did to encounter it:

image

Probably has something to do with streams?

This will be harder to do for chained expessions future.field_or_resolved_future.execute_new_future()? that should have been future.await.field_or_resolved_future.execute_new_future().await?.

@nikomatsakis Looks like @wycats's error was the same, it's just that the output type of the future is more complicated (Pin<Box<Stream<Item = Value> + Send + 'static>>).

Hi, I'd be really interested in working on this

@doctorn awesome! Sending @rustbot claim should assign you to the issue and feel free to start a topic in the #wg-async-foundations stream if you need a hand.

@rustbot claim

Can we close this now @doctorn ? - seems like this is now done.

Unfortunately not... I haven't had a lot of time for the past few weeks, but basically all cases similar to this one that @estebank pointed out still aren't covered.

future.field_or_resolved_future.execute_new_future()? that should have been
future.await.field_or_resolved_future.execute_new_future().await?.

I've made some progress towards this, but there are some complicated trait predicates involved in solving the ? case. I brought this up in #t-compiler/help but I'm not aware of anyone who knows how to solve this at the moment.

I fear that that case will remain open for a while, as it'll be hard to get right.

I was thinking that this issue was fairly similar to #63100.

It is essentially the same issue. It boils down to altering the trait inference code to give diagnostics when certain necessary trait predicates don鈥檛 hold. For example if you consider the question mark case:

async fn foo() -> Result<(), ()> {
    Ok(())
}

async fn bar() -> Result<(), ()> {
    foo()?;
    Ok(())
}

The error here arises because Future<Output=Result<(), ()>> does not implement std::ops::Try; however, it鈥檚 obvious to us that the Output type does, so we want to await here.

What we鈥檇 want to do is say that any time type T can鈥檛 be shown to implement an arbitrary trait (say Baz), if we can show that T: Future<Output=U> where U: Baz suggest .await. Similarly, for functions rather than futures if we can show that T: Fn() -> U where U: Baz suggest calling it.

Long story short, we need to verify if a trait bound holds for a projected type; however, I don鈥檛 think you can do this easily.

63100 only points out cases where we have a concrete expected type which are the same cases I covered with #62067. Covering expected trait bounds is where it gets sticky.

From #62067, these are the cases still missing:

  • [x] Return places for functions
  • [ ] Field access
  • [ ] Method invocation

In bodies that aren't async, .await is not suggested, but maybe we should mention that we're _not_ in an async block and that the future _could_ have been awaited. I guess this is likely to be a common wip error during development.

@rustbot release-assignment

@doctorn mentioned they were pretty busy

Here's another example where suggesting .await would be helpful (playground):

async fn a() -> Result<(), ()> {
    Ok(())
}

fn main() {
    match a() {
        Ok(()) => (),
        Err(()) => (),
    }
}

@csmoe Thanks for improving on this with #71948. Do you want to continue working on this, or release the assignment for now?

@tmandry I'll try to fix them all, more commits pending.

From #62067, these are the cases still missing:

* [ ]  Return places for functions

* [ ]  Field access

* [ ]  Method invocation

In bodies that aren't async, .await is not suggested, but maybe we should mention that we're _not_ in an async block and that the future _could_ have been awaited. I guess this is likely to be a common wip error during development.

@estebank could you write a case of return-place-for-functions? I'm not sure about this one, since await seems already suggested

Indeed. I'm not sure if it is due to an improvement since august last year, or just a mistake on my part. Either way, we are in a good place today :)

Ping from wg-async-foundations triage. @csmoe are you still working on this?

@tmandry yes, in slow progress since time is tough these days.

Was this page helpful?
0 / 5 - 0 ratings