Open
Description
Code
use std::error::Error;
trait ErrorReport {
fn report(&self);
}
impl<E: Error + Sized> ErrorReport for E {
fn report(&self) { /* real impl does something requiring Self: Sized */ }
}
impl ErrorReport for dyn Error /* missing marker traits + Send + Sync */ {
fn report(&self) {}
}
trait ErrorReportWithoutBlanket {
fn report_without_blanket(&self);
}
impl ErrorReportWithoutBlanket for dyn Error /* missing marker traits + Send + Sync */ {
fn report_without_blanket(&self) {}
}
#[derive(Debug, thiserror::Error)]
#[error("my error")]
struct MyError;
fn main() {
MyError.report();
let ae: anyhow::Error = MyError.into();
// > error[E0599]: the method `report` exists for trait object `dyn Error + Send + Sync`,
// > but its trait bounds were not satisfied
// this message ^ if you read it carefully does highlight the problem,
// namely that dyn Error != dyn Error + Send + Sync
(*ae).report();
(*ae).report_without_blanket();
// > error[E0599]: the method `report` exists for struct `Error`,
// > but its trait bounds were not satisfied
// the programmer is hoping to get the method via the deref so this isn't relevant
//
// > ::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/error.rs:32:1
// > |
// > 32 | pub trait Error: Debug + Display {
// > | --------------------------------
// > | |
// > | doesn't satisfy `dyn std::error::Error + Send + Sync: ErrorReport`
// > | doesn't satisfy `dyn std::error::Error + Send + Sync: Sized`
// this is a complaint that the blanket impl isn't relevant:
// as the programmer can see from the complaint about Sized
//
// > ::: /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/anyhow-1.0.75/src/lib.rs:374:1
// > |
// > 374 | pub struct Error {
// > | ----------------
// > | |
// > | doesn't satisfy `anyhow::Error: ErrorReport`
// > | doesn't satisfy `anyhow::Error: std::error::Error`
// > |
// > note: the following trait bounds were not satisfied:
// > `anyhow::Error: std::error::Error`
// > `dyn std::error::Error + Send + Sync: Sized`
// > --> src/main.rs:6:6
// > |
// > 6 | impl<E: Error + Sized> ErrorReport for E {
// > | ^ ^^^^^ ^^^^^ ----------- -
// > | | | |
// > | | | unsatisfied trait bound introduced here
// > | | unsatisfied trait bound introduced here
// > | unsatisfied trait bound introduced here
// > help: consider relaxing the type parameter's implicit `Sized` bound
// > |
// > 6 | impl<E: ?Sized + Error + Sized> ErrorReport for E {
// > | ++++++++
// this is an essay about the same thing.
//
// Note there is no message explaining why the impl for dyn Error isn't relevant.
ae.report();
// > error[E0599]: no method named `report_without_blanket` found for struct `anyhow::Error` in the current scope
// > --> src/main.rs:78:8
// > |
// > 78 | ae.report_without_blanket();
// > | ^^^^^^^^^^^^^^^^^^^^^^ method not found in `Error`
// > |
// > = help: items from traits can only be used if the trait is implemented and in scope
// > note: `ErrorReportWithoutBlanket` defines an item `report_without_blanket`, perhaps you need to implement it
// > --> src/main.rs:13:1
// > |
// > 13 | trait ErrorReportWithoutBlanket {
// > | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// No mention of the impl for the Deref.
ae.report_without_blanket();
}
Current output
error[E0599]: the method `report` exists for trait object `dyn Error + Send + Sync`, but its trait bounds were not satisfied
--> src/main.rs:32:11
|
32 | (*ae).report();
| ^^^^^^ method cannot be called on `dyn Error + Send + Sync` due to unsatisfied trait bounds
|
::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/error.rs:32:1
|
32 | pub trait Error: Debug + Display {
| --------------------------------
| |
| doesn't satisfy `dyn std::error::Error + Send + Sync: ErrorReport`
| doesn't satisfy `dyn std::error::Error + Send + Sync: Sized`
|
note: trait bound `dyn std::error::Error + Send + Sync: Sized` was not satisfied
--> src/main.rs:6:6
|
6 | impl<E: Error + Sized> ErrorReport for E {
| ^ ^^^^^ ----------- -
| | |
| | unsatisfied trait bound introduced here
| unsatisfied trait bound introduced here
help: consider relaxing the type parameter's implicit `Sized` bound
|
6 | impl<E: ?Sized + Error + Sized> ErrorReport for E {
| ++++++++
error[E0599]: no method named `report_without_blanket` found for trait object `(dyn std::error::Error + Send + Sync + 'static)` in the current scope
--> src/main.rs:33:11
|
33 | (*ae).report_without_blanket();
| ^^^^^^^^^^^^^^^^^^^^^^ method not found in `dyn Error + Send + Sync`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `ErrorReportWithoutBlanket` defines an item `report_without_blanket`, perhaps you need to implement it
--> src/main.rs:13:1
|
13 | trait ErrorReportWithoutBlanket {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0599]: the method `report` exists for struct `Error`, but its trait bounds were not satisfied
--> src/main.rs:75:8
|
75 | ae.report();
| ^^^^^^ method cannot be called on `Error` due to unsatisfied trait bounds
|
::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/error.rs:32:1
|
32 | pub trait Error: Debug + Display {
| --------------------------------
| |
| doesn't satisfy `dyn std::error::Error + Send + Sync: ErrorReport`
| doesn't satisfy `dyn std::error::Error + Send + Sync: Sized`
|
::: /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/anyhow-1.0.75/src/lib.rs:374:1
|
374 | pub struct Error {
| ----------------
| |
| doesn't satisfy `anyhow::Error: ErrorReport`
| doesn't satisfy `anyhow::Error: std::error::Error`
|
note: the following trait bounds were not satisfied:
`anyhow::Error: std::error::Error`
`dyn std::error::Error + Send + Sync: Sized`
--> src/main.rs:6:6
|
6 | impl<E: Error + Sized> ErrorReport for E {
| ^ ^^^^^ ^^^^^ ----------- -
| | | |
| | | unsatisfied trait bound introduced here
| | unsatisfied trait bound introduced here
| unsatisfied trait bound introduced here
help: consider relaxing the type parameter's implicit `Sized` bound
|
6 | impl<E: ?Sized + Error + Sized> ErrorReport for E {
| ++++++++
error[E0599]: no method named `report_without_blanket` found for struct `anyhow::Error` in the current scope
--> src/main.rs:91:8
|
91 | ae.report_without_blanket();
| ^^^^^^^^^^^^^^^^^^^^^^ method not found in `Error`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `ErrorReportWithoutBlanket` defines an item `report_without_blanket`, perhaps you need to implement it
--> src/main.rs:13:1
|
13 | trait ErrorReportWithoutBlanket {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Desired output
Additional output should ideally say something like:
help: ErrorReport is implemented for dyn Error
whereas anyhow::Error dereferences to dyn Error + Send + Sync
which is not the same type. Perhaps you should change the trait implementation
or dereference implementation so that the trait bounds match.
Rationale and extra context
Implementing a trait for dyn Trait is a common workaround for various missing affordances. When this happens in combination with expectations of Deref, it would be nice if the compiler would explain the "near miss".
Other cases
This near miss logic should probably be applied sparingly to avoid generating a lot of noise from deref chains. If the miss is purely due to Send/Sync bounds discrepancies, it is probably very relevant.
Anything else?
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e392d92f7912e5b7db2ff16ee580601a