Skip to content

Trait bounds on associated types of subtraits don't work correctly #54149

Closed

Description

@sicking

The following code does not compile:

trait MyTrait1 {
    type X;
    fn get_val(&self) -> Self::X;
}

trait MyTrait2: MyTrait1 where <Self as MyTrait1>::X: Into<u32> {}

struct MyStruct(u32);

impl MyTrait1 for MyStruct {
    type X = u32;
    fn get_val(&self) -> Self::X { self.0 }
}
impl MyTrait2 for MyStruct {}

fn myfunc<T: MyTrait2>(v: &T) {
    println!("{}", Into::<u32>::into(v.get_val()));
}

fn main() {
    let s = MyStruct(10);
    myfunc(&s);
}

It fails with

error[E0277]: the trait bound `u32: std::convert::From<<T as MyTrait1>::X>` is not satisfied
  --> src/main.rs:16:1
   |
16 | / fn myfunc<T: MyTrait2>(v: &T) {
17 | |     println!("{}", Into::<u32>::into(v.get_val()));
18 | | }
   | |_^ the trait `std::convert::From<<T as MyTrait1>::X>` is not implemented for `u32`
   |
   = help: consider adding a `where u32: std::convert::From<<T as MyTrait1>::X>` bound
   = note: required because of the requirements on the impl of `std::convert::Into<u32>` for `<T as MyTrait1>::X`
note: required by `MyTrait2`
  --> src/main.rs:6:1
   |
6  | trait MyTrait2: MyTrait1 where <Self as MyTrait1>::X: Into<u32> {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In fact, I haven't been able to come up with any syntax for myfunc which uses a trait bound using MyTrait2 that will compile once the trait bound X exists.

Changing the declaration of MyTrait2 to trait MyTrait2: MyTrait1<X=u32> {} does work, so it's clearly possible for super traits to put constraints on the associated types of subtraits. However I've only been able to create constraints that require the associated types to be of a particular type, not to require that they implement particular traits. I also tried trait MyTrait2: MyTrait1<X: Into<u32>> {}, however that just yielded a syntax error.

It's quite possible that I'm misunderstanding the meaning of the syntax in the original example above, and that I should create an RFC for supporting what I'm trying to accomplish above.

Activity

  1. rkarp commented on Oct 14, 2018

    @rkarp
    Contributor

    This is another issue with probably the same underlying cause as #24159. Here's how to fix your code:

    trait MyTrait1 {
        type X;
        fn get_val(&self) -> Self::X;
    }
    
    trait MyTrait2: MyTrait1 where <Self as MyTrait1>::X: Into<u32> {}
    
    struct MyStruct(u32);
    
    impl MyTrait1 for MyStruct {
        type X = u32;
        fn get_val(&self) -> Self::X { self.0 }
    }
    impl MyTrait2 for MyStruct {}
    
    
    fn myfunc<T>(v: &T)
    where T: MyTrait2,
          <T as MyTrait1>::X: Into<u32>,
    {
        println!("{}", Into::<u32>::into(v.get_val()));
    }
    
    fn main() {
        let s = MyStruct(10);
        myfunc(&s);
    }

    Funnily enough there is no error when you remove myfunc altogether. I guess rustc believes it's fine to declare Mytrait2 as long as you never actually use it.

  2. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Aug 5, 2019
  3. added
    D-confusingDiagnostics: Confusing error or lint that should be reworked.
    on Oct 8, 2019
  4. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    on Jun 11, 2020
  5. estebank commented on Jun 15, 2020

    @estebank
    Contributor

    Current output:

    error[E0277]: the trait bound `u32: std::convert::From<<T as MyTrait1>::X>` is not satisfied
      --> src/main.rs:16:14
       |
    6  | trait MyTrait2: MyTrait1 where <Self as MyTrait1>::X: Into<u32> {}
       |                                                       --------- required by this bound in `MyTrait2`
    ...
    16 | fn myfunc<T: MyTrait2>(v: &T) {
       |              ^^^^^^^^ the trait `std::convert::From<<T as MyTrait1>::X>` is not implemented for `u32`
       |
       = note: required because of the requirements on the impl of `std::convert::Into<u32>` for `<T as MyTrait1>::X`
    

    A possible solution is to incorporate a new type param to restrict MyTrait2::X with:

    fn myfunc<I: Into<u32>, T: MyTrait2<X = I>>(v: &T) {
        println!("{}", Into::<u32>::into(v.get_val()));
    }
  6. estebank commented on Feb 2, 2021

    @estebank
    Contributor

    Triage: no change.

  7. estebank commented on Aug 3, 2023

    @estebank
    Contributor

    Current output suggests a fix:

    error[E0277]: the trait bound `u32: From<<T as MyTrait1>::X>` is not satisfied
      --> src/main.rs:16:14
       |
    16 | fn myfunc<T: MyTrait2>(v: &T) {
       |              ^^^^^^^^ the trait `From<<T as MyTrait1>::X>` is not implemented for `u32`
       |
       = note: required for `<T as MyTrait1>::X` to implement `Into<u32>`
    note: required by a bound in `MyTrait2`
      --> src/main.rs:6:55
       |
    6  | trait MyTrait2: MyTrait1 where <Self as MyTrait1>::X: Into<u32> {}
       |                                                       ^^^^^^^^^ required by this bound in `MyTrait2`
    help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
       |
    16 | fn myfunc<T: MyTrait2>(v: &T) where u32: From<<T as MyTrait1>::X> {
       |                               +++++++++++++++++++++++++++++++++++
    
  8. robertbastian commented on Aug 28, 2023

    @robertbastian
    Contributor

    Why does myfunc need the <T as MyTrait1>::X: Into<u32> bound? It already has T: MyTrait2, which implies <T as MyTrait1>::X: Into<u32>.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-trait-systemArea: Trait systemC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-confusingDiagnostics: Confusing error or lint that should be reworked.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