Blanket impl for all objects whose associated type implement a specific trait

This minimal example ( playground ):

pub trait Named {
    fn name() -> &'static str;
}

pub trait Backend<'a>: Named {
    type Settings: Settings;
}

pub trait Settings {}

impl<'a, N: Settings + Named, T: Backend<'a, Settings = N>> Named for T {
    fn name() -> &'static str {
        N::name()
    }
}

Yields this error:

error[E0207]: the type parameter `N` is not constrained by the impl trait, self type, or predicates
  --> src/lib.rs:11:10
   |
11 | impl<'a, N: Settings + Named, T: Backend<'a, Settings = N>> Named for T {
   |          ^ unconstrained type parameter

Isn’t it enough constrained in the way that its used as Settings = N? What is even more puzzling to me is that it compiles when I remove the life time ( playground ):

pub trait Named {
    fn name() -> &'static str;
}

pub trait Backend: Named {
    type Settings: Settings;
}

pub trait Settings {}

impl<N: Settings + Named, T: Backend<Settings = N>> Named for T {
    fn name() -> &'static str {
        N::name()
    }
}

Any ideas?

The idea of the code is that a Backend can either have a Settings type that determines the name of the Backend or if it has to specify it by implementing Named manually.

I think the compiler is trying to understand which lifetime 'a is referring to, but in your case it’s any lifetime. You can express it using HRTB:

impl<N: Settings + Named, T> Named for T
where
    T: for<'a> Backend<'a, Settings = N>,
{
    fn name() -> &'static str {
        N::name()
    }
}
1 Like

You don’t even need N, you can put the constraints directly on the associated type.

impl<'a, T: Backend<'a>> Named for T
where
    T::Settings: Settings + Named,
{
    fn name() -> &'static str {
        T::Settings::name()
    }
}
4 Likes

Great advice! Both solutions do work. But now a new error surfaced. It seems that the blanket impl is also applying to a T where the T::Settings does not implement Named. Consequently it says that there are conflicting implementations. I added this struct. ( playground ):

struct CoolBackend {}

impl Named for CoolBackend {
    fn name() -> &'static str {
        "CoolBackend"
    }
}

impl Settings for () {}

impl<'a> Backend<'a> for CoolBackend {
    type Settings = ();
}

Got this error:

error[E0119]: conflicting implementations of trait `Named` for type `CoolBackend`:
  --> src/lib.rs:26:1
   |
11 | / impl<'a, T: Backend<'a>> Named for T
12 | | where
13 | |     T::Settings: Named + Settings
14 | | {
...  |
17 | |     }
18 | | }
   | |_- first implementation here
...
26 |   impl Named for CoolBackend {
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `CoolBackend`

I guess what I am trying to do falls under specialization. Maybe it is my C++ way of thinking that I always hit this roadblock. I have to come up with other abstractions.

I just thought because this blanket impl does not apply at it does not count.