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.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.