How to add a trait bound to a generic associtated type of a supertrait

Hi!

I have following traits (example on playground):

    trait ProductTrait {
        fn name(&self) -> &str;
    }

    trait ProducerTrait {
        type Product<'p>: ProductTrait;

        fn produce(&self, name: &str) -> Self::Product<'_>;
    }

    trait LoggingProducerTrait : ProducerTrait
    {

        fn produce_and_log(&self, name: &str) -> Self::Product<'_> {
            let product = self.produce(name);
            println!("Produced '{}'", product);
            product
        }
    }

For the LogginProducerTrait I want to add an additional trait bound to the generic associated type Product of its supertrait ProducerTrait.
I tried adding the following where-clause, but that would not compile:

    trait LoggingProducer : ProducerTrait
        where Self::Product: Display 

with the following message:

error[E0107]: missing generics for associated type `ProducerTrait::Product`
  --> src/main.rs:16:17
   |
16 |     where Self::Product: Display
   |                 ^^^^^^^ expected 1 lifetime argument
   |
note: associated type defined here, with 1 lifetime parameter: `'p`

Following the instruction I added the lifetime:

trait LoggingProducerTrait : ProducerTrait
    where for <'p> Self::Product<'p> : Display

This compiles, but when I implement Display for my product Car, it does not work. I get the following error:

error[E0277]: `<CarFactory as ProducerTrait>::Product<'p>` doesn't implement `std::fmt::Display`
  --> src/main.rs:57:6
   |
15 | trait LoggingProducerTrait : ProducerTrait
   |       -------------------- required by a bound in this
16 |     where for <'p> Self::Product<'p> : Display
   |                                        ------- required by this bound in `LoggingProducerTrait`
...
57 | impl LoggingProducerTrait for CarFactory {}
   |      ^^^^^^^^^^^^^^^^^^^^ `<CarFactory as ProducerTrait>::Product<'p>` cannot be formatted with the default formatter
   |
   = help: the trait `for<'p> std::fmt::Display` is not implemented for `<CarFactory as ProducerTrait>::Product<'p>`

What am I doing wrong? Shouldn't I use HKTB for this? Or is my Display implementation for Car wrong?

Thanks for your help!

1 Like

I feel like your code should work: to me this looks like a bug in the compiler...

As a workaround for your case, you can move the Display bound:

trait LoggingProducerTrait: ProducerTrait {
    fn produce_and_log<'a>(&'a self, name: &str) -> Self::Product<'a>
    where
        Self::Product<'a>: Display,
    {
        let product = self.produce(name);
        println!("Produced '{}'", product);
        product
    }
}

With this your example compiles :slightly_smiling_face:

Alternatively, if you want to avoid HKTB, maybe you could define the Producer traits to be implemented on a reference ? Something like this:

trait ProducerTrait<'p> {
    type Product: ProductTrait;

    fn produce(self, name: &str) -> Self::Product;
}

trait LoggingProducerTrait<'p>: ProducerTrait<'p>
where Self::Product: Display
{ /* */ }

impl<'p> ProducerTrait<'p> for &'p CarFactory { /* ... */ }
impl<'p> LoggingProducerTrait<'p> for &'p CarFactory {}

(playground)

1 Like

Thank you for your answer!

Here’s a kind-of “solution” that might be closer to what you were asking for:

Rust Playground

2 Likes