Why can't I add a blanket impl on a trait with a type parameter?

前端 未结 2 997
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-04 01:49

Consider these two traits:

pub trait Foo {
    fn new(arg: u32) -> Self;
}

pub trait Bar

: Foo { fn with_parameter(arg: u32, parameter: P) -&

相关标签:
2条回答
  • 2020-12-04 02:13

    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)?
    0 讨论(0)
  • 2020-12-04 02:22

    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.

    0 讨论(0)
提交回复
热议问题