rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.28k stars 12.72k forks source link

Explain how to fix “no field on type” on a TAIT / ATPIT typed value #113581

Open kpreid opened 1 year ago

kpreid commented 1 year ago

Code

#![feature(impl_trait_in_assoc_type)]

pub trait Transaction {
    type Check: 'static;
    fn check(&self) -> Self::Check;
    fn commit(&self, check: Self::Check);
}

pub mod foo {
    pub struct Foo;

    struct FooC {
        field: (),
    }

    impl super::Transaction for Foo {
        type Check = impl Sized;

        fn check(&self) -> Self::Check {
            FooC { field: () }
        }

        fn commit(&self, check: Self::Check) {
            assert_eq!(check.field, ());
        }
    }
}

Current output

error[E0609]: no field `field` on type `<Foo as Transaction>::Check`
  --> src/lib.rs:24:30
   |
24 |             assert_eq!(check.field, ());
   |                              ^^^^^

For more information about this error, try `rustc --explain E0609`.
error: could not compile `playground` (lib) due to previous error

Desired output

help: to access fields of the underlying type `FooC`, coerce the value to it:
      `(check as FooC).field`

Rationale and extra context

If users are not given this advice, it will not be obvious how to proceed; they may think that it is not even possible. For me at least, it is obvious only in hindsight that

The alternate mental model I was working with before I understood the problem was something like:

so check should be of type FooC already. Now that I understand what's actually going on, it makes much more sense that the compiler would work this way (scope-dependent type equality would be a nightmare), but it wasn't obvious when I started.

Other cases

No response

Anything else?

This exact same problem happens with TAIT; I just think it's just a little less clear and more important for associated types in traits.

Another detail to consider is what the output should be when the underlying type does not have such a field (or method). I think there should still be a similar hint, but phrased differently — in general, this hint should show up whenever

@rustbot label +A-impl-trait +F-type_alias_impl_trait

kpreid commented 1 year ago

A solution that might be more elegant in particular code is to destructure the argument — let FooC { field } = check;. That's harder to suggest automatically, however, since it involves introducing a new statement and variables.