Why can't I add a blanket impl on a trait with a type parameter?
The problem is that a single type could implement Bar<P>
for multiple values of P
. If you had a struct Baz
that implemented Bar<i32>
and Bar<String>
, which type should Foo::new
use for P
?
The only solution is to ensure that a single type cannot implement Bar
more than once (if that's not what you want, then you have a flaw in your design!). To do so, we must replace the P
type parameter with an associated type.
pub trait Bar: Foo {
type Parameter;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self;
}
impl<T> Foo for T
where
T: Bar,
T::Parameter: Default,
{
fn new(arg: u32) -> Self {
Self::with_parameter(arg, T::Parameter::default())
}
}
An implementation of Bar
would look like this:
struct Baz;
impl Bar for Baz {
type Parameter = i32;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self {
unimplemented!()
}
}
See also:
- Why do I get "the type parameter is not constrained" when creating a blanket implementation for a closure trait (Fn)?
I've broken down and extended Francis's explanation of why the code does not compile. I may not be the smartest kid on the block, but it took me way too long to understand his concise reasoning.
Let's create Baz
, which implements Bar
in 2 variants: i32
and String
:
struct Baz;
impl Bar<i32> for Baz { /* ... */ }
impl Bar<String> for Baz { /* ... */ }
Type dependency graph after blanket impl takes effect:
-> trait Bar<i32> -> trait Foo (with i32 baked-in)
struct Baz
-> trait Bar<String> -> trait Foo (with String baked-in)
We end up with 2 different implementations of Foo
: with baked-in i32
and with baked-in String
.
When we write <Baz as Foo>::new()
, compiler can't tell which version of Foo
we mean; they are indistinguishable.
The rule of a thumb is that trait A can have blanket implementation for trait B only if trait A is generic over all generic parameters of trait B.