Description
I just spent some time being quite confused by an error message I got when mistakenly using an async fn
whose return type contains an impl Trait
in multiple branches of a match
statement. On my local toolchain, which is still on 1.62, the corresponding output is:
error[E0308]: `match` arms have incompatible types
--> tools/src/bin/volt-test-device-comms.rs:243:35
|
236 | let read_fut = match self.register {
| ------------------- `match` arms have incompatible types
237 | Discrete(reg) => with_timeout(client.read_discrete_inputs(reg, 1))
| ______________________________________-
238 | | .await
239 | | .map(|res| res.map(|_| ())),
| |___________________________________________________- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
240 | Coil(reg) => with_timeout(client.read_coils(reg, 1))
| __________________________________-
241 | | .await
242 | | .map(|res| res.map(|_| ())),
| |___________________________________________________- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
243 | Input(reg) => with_timeout(client.read_input_registers(reg, 1))
| ___________________________________^
244 | | .await
245 | | .map(|res| res.map(|_| ())),
| |___________________________________________________^ expected `bool`, found `u16`
...
307 | async fn with_timeout<O, F: Future<Output = O>>(future: F) -> Result<O, impl std::error::Error> {
| ----------------------
| |
| the expected opaque type
| the found opaque type
|
= note: expected enum `Result<_, impl std::error::Error>` (`bool`)
found enum `Result<_, impl std::error::Error>` (`u16`)
(This is using tokio_modbus
, there's a reduced repro below.) On current stable (1.64) and nightly (1.66.0-nightly (2022-09-25 f5193a9)), the message seems to have improved with some additional information about what other types the compiler has looked at while trying to match the opaque type. So currently, given the following code (playground):
use std::fmt;
use std::future::Future;
#[derive(Debug, PartialEq)]
struct MyError(());
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "my message")
}
}
impl std::error::Error for MyError {}
fn main() {}
async fn test() {
let _ = match 0u16 {
0 => maybe(one(), true).await.map(|res| res.map(|_| ())),
_ => maybe(r#true(), true).await.map(|res| res.map(|_| ())),
};
}
async fn maybe<O, F: Future<Output = O>>(future: F, run: bool) -> Result<O, impl std::error::Error> {
if !run {
Err(MyError(()))
} else {
Ok(future.await)
}
}
async fn one() -> Result<usize, std::io::Error> {
Ok(1)
}
async fn r#true() -> Result<bool, std::io::Error> {
Ok(true)
}
The output is:
error[[E0308]](https://doc.rust-lang.org/stable/error-index.html#E0308): `match` arms have incompatible types
--> src/main.rs:20:14
|
18 | let _ = match 0u16 {
| _____________-
19 | | 0 => maybe(one(), true).await.map(|res| res.map(|_| ())),
| | --------------------------------------------------- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
20 | | _ => maybe(r#true(), true).await.map(|res| res.map(|_| ())),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
21 | | };
| |_____- `match` arms have incompatible types
...
24 | async fn maybe<O, F: Future<Output = O>>(future: F, run: bool) -> Result<O, impl std::error::Error> {
| ----------------------
| |
| one of the expected opaque types
| one of the found opaque types
|
note: while checking the return type of the `async fn`
--> src/main.rs:32:19
|
32 | async fn one() -> Result<usize, std::io::Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, one of the expected opaque types
note: while checking the return type of the `async fn`
--> src/main.rs:35:22
|
35 | async fn r#true() -> Result<bool, std::io::Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, one of the found opaque types
= note: expected enum `Result<_, impl std::error::Error>` (`usize`)
found enum `Result<_, impl std::error::Error>` (`bool`)
For more information about this error, try `rustc --explain E0308`.
While this gives more context on what code might be involved in the error,
- the primary diagnostic still says "expected
usize
, foundbool
", which might be true in some sense, but the actual mismatched types are different instances ofimpl Error
whose only relation to the mentioned primitive types is that the latter occur in the type parameters to the function that produces theimpl Error
, - the source location for "one of the expected opaque types" and "one of the found opaque types" is identical. Both point to the code that declares the
impl Error
return type, which is always confusing because it makes it seem that expected and found are the same (in which case, why is there an error), - the
note
s claim that theFuture::Output
parameters of theasync fn
s used to invokemaybe
are expected/found opaque types. This is still confusing to me, because (as in the case above) the error is about theimpl Error
, and in this caseOutput = Result<usize, std::io::Error>
, which is neither opaque nor the problem (the only thing that would somewhat make sense to me here is thatasync fn
desugars toimpl Future
, which is opaque. But that's not present in the diagnostic at all), - the
(`bool`)
and(`usize`)
in the finalnote
were confusing to me, because they don't appear anywhere in the type (neither the printed abbreviation nor the real type, which at the point of the error is areadyResult<Result<(), io::Error>, impl Error>
) and the message doesn't say what they mean and what they've got to do with the error, and - nowhere in the message is it mentioned that
impl Trait
return types are different when instantiating generic functions with different parameters- with my actual issue, this was even more confusing to me because the first two branches did not produce an error, even though the futures, also a generic parameter of the function returning the opaque type, were different. So I would have expected that to already cause the error if that was the problem. This seems better on current stable, where adding a second branch with an
async fn two() -> Result<usize, io::Error>
produces an error which points to the locations of the two functions (playground)
- with my actual issue, this was even more confusing to me because the first two branches did not produce an error, even though the futures, also a generic parameter of the function returning the opaque type, were different. So I would have expected that to already cause the error if that was the problem. This seems better on current stable, where adding a second branch with an
Possible output that would have helped me better identify the issue:
error[[E0308]](https://doc.rust-lang.org/stable/error-index.html#E0308): `match` arms have incompatible types
--> src/main.rs:20:14
|
18 | let _ = match 0u16 {
| _____________-
19 | | 0 => maybe(one(), true).await.map(|res| res.map(|_| ())),
| | --------------------------------------------------- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
20 | | _ => maybe(r#true(), true).await.map(|res| res.map(|_| ())),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `impl std::error::Error` is different here
21 | | };
| |_____- `match` arms have incompatible types
...
24 | async fn maybe<O, F: Future<Output = O>>(future: F, run: bool) -> Result<O, impl std::error::Error> {
| ----------------------
| |
| opaque type defined here
|
= note: when returning a type containing `impl Trait` from a generic function, its concrete type is different if the function is called with different generic arguments
note: while checking the return type of the `async fn`
--> src/main.rs:32:19
|
32 | async fn one() -> Result<usize, std::io::Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, which is `O` in <src/main.rs:19:20>
note: while checking the return type of the `async fn`
--> src/main.rs:35:22
|
35 | async fn r#true() -> Result<bool, std::io::Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, which is `O` in <src/main.rs:20:20>
= note: mismatch between `usize` and `bool`
For more information about this error, try `rustc --explain E0308`.
For completeness, the error is resolved as expected by replacing the opaque type with its concrete type.
The latest change to the message for this error that I could find is #63167, but that doesn't yet include the (`bool`)
(it just says "opaque type" and adds a source location). #93519 might be similar.