Skip to content

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

Open
@kpreid

Description

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 incoming check argument is of a different type than the outgoing return value, and
  • a type coercion would solve the problem here.

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

  • Self::Check is an alias for a particular impl Sized
  • which, only this impl or module knows (being in the defining scope of the impl Trait), is an alias for FooC

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

  • an opaque type is used,
  • in a place where the coercion would be allowed,
  • at a use site which does not itself cause a coercion already (e.g. a generic rather than concrete function parameter).

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.F-impl_trait_in_assoc_type`#![feature(impl_trait_in_assoc_type)]`F-type_alias_impl_trait`#[feature(type_alias_impl_trait)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions