Strange useless compiler advice

While I try to build my updated to async code I got the strange compiler diagnostic message:

% cargo check
    Checking veml7700 v0.2.0 (/Users/nis/Develop/crates/veml7700)
error[E0599]: no method named `map_err` found for associated type `impl Future<Output = Result<(), <I2C as embedded_hal_async::i2c::ErrorType>::Error>>` in the current scope
   --> src/device_impl.rs:299:71
    |
298 | /         self.i2c
299 | |             .write_read(DEVICE_ADDRESS, &[register], &mut data).await.map_err(I2C::Error::into)?;
    | |                                                                      -^^^^^^^ method not found in `impl Future<Output = Result<(), <I2C as ErrorType>::Error>>`
    | |______________________________________________________________________|
    |
    |
help: consider `await`ing on the `Future` and calling the method on its `Output`
    |
299 |             .write_read(DEVICE_ADDRESS, &[register], &mut data).await.await.map_err(I2C::Error::into)?;
    |                                                                       ++++++

For more information about this error, try `rustc --explain E0599`.

Questions like this belong on https://users.rust-lang.org. And you will need to give much more detail than this

2 Likes

I asked about why in explanation compiler considers to add await after await, this looks like useless suggestion, isn't it?

It looks like you have nested futures, according to the error

9 Likes

Did you expect to use map_err from FutureExt, or the Result?

I found the root cause of this message, but I expected not see useless message from compiler.

Please, can you use some softer language? We are there to discuss and collaborate, after all.

4 Likes

I don't think "useless" is too harsh if it's a factual description. But, from that error message, I would assume that the first .await returned another future, which would make .await.await a reasonable suggestion.

If it WASN'T a future, I think this error would be a compiler bug – but we would need more context to prove that it wasn't.

If it was indeed a future, then perhaps there could be a better error message, but it's hard to say because this error is in a function that merely consumes the double-future, so, even if the double-future wasn't intended, you wouldn't be able to fix that by changing this function (where the error is). Having the error explicitly say "note: this is a future that outputs another future" could help, I suppose.

6 Likes

Suddenly I tried to add .await many times and it looked like compiler got me the same advice each time I added another .await. I create as small MVP as possible: consider `await`ing on the `Future` and calling the method on its `Output` · GitHub

@Ddystopia I'm so sorry if you think my language to rude.

The maybe_async macro is removing all the .awaits from your function, so what the compiler sees is effectively try_to_fail().unwrap(). The suggestion of adding .await would thus be correct, if not for the fact that the maybe_async macro would remove that too. I don't think it's reasonable to expect the compiler to understand that the maybe_async will remove any .await and thus change the suggestion though.

12 Likes

Yeah, this problem is unrelated to await and extremely related to how errors are reported when procedural macros have modified the code.

I do think there might be an improvement to make here. I've seen many confusing error messages when the compiler quotes the source code while reporting an error in code generated by a procedural macro. It's not obvious what the best solution would be, but at least I feel like there should be a way for the error message to alert the user that they're not looking at the exact code with the error in it!

7 Likes

It would be nice if a procmacro crate could register an handler for compiler errors, so that the crates like maybe-async could match such errors and replace them with their own.

I'm not sure if letting them replace compiler-produced errors is a great idea, but a mechanism to add custom help: or note: annotations makes a lot of sense to me.

2 Likes

That is sort of what the diagnostic namespace attribute does, for example Introduce a `#[diagnostic::on_unknown_item]` attribute by weiznich · Pull Request #136781 · rust-lang/rust · GitHub is (an attempt at creating) an attribute that catches import errors and replaces them.

I'm very wary of a general mechanism to rethrow compiler errors though; setting aside the implementation questions the problem is that if this is emitted from macros then the macro author will end up rethrowing errors originating from code they haven't written. Users will write stuff you didn't foresee and along with evolving errors and syntax and so on, it seems to me something that will age poorly.

Like what e said, a mechanism to just add a note seems less problematic, at least from a "what will people end up doing with this" perspective.

Seems the best solution is to simply expand the macro.