为什么我不能在带有类型参数的特征上添加毯子impl?

时间:2017-03-05 21:03:18

标签: generics rust traits type-parameter

考虑以下两个特征:

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

pub trait Bar<P>: Foo {
    fn with_parameter(arg: u32, parameter: P) -> Self;
}

我想添加毯子impl:

impl<T: Bar<P>, P: Default> Foo for T {
    fn new(arg: u32) -> Self {
        Self::with_parameter(arg, P::default())
    }
}

但是我收到了编译错误:

error[E0207]: the type parameter `P` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:17
  |
9 | impl<T: Bar<P>, P: Default> Foo for T {
  |                 ^ unconstrained type parameter

我认为我收到此错误是因为我违反了特质一致性规则,但我并不完全明白这会违反什么规则。为什么不允许这种模式?而且,更重要的是,我可以实现我想要的而不会出错吗?

2 个答案:

答案 0 :(得分:7)

问题是单个类型可以为Bar<P>的多个值实现P。如果您有一个实现BazBar<i32>的结构Bar<String>Foo::new应使用哪种类型?{/ p>

唯一的解决方案是确保单个类型不能多次实现P(如果这不是您想要的,那么您的设计就有缺陷!)。为此,我们必须将Bar类型参数替换为关联类型。

P

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()) } } 的实现如下所示:

Bar

另见:

答案 1 :(得分:1)

我已经细分并扩展了Francis's explanation为什么代码无法编译的原因。我可能不是最聪明的孩子,但是我花了很长时间才了解他的简洁推理。

让我们创建Baz,它以两个变体实现Bari32String

struct Baz;

impl Bar<i32> for Baz { /* ... */ }

impl Bar<String> for Baz { /* ... */ }

一揽子隐含生效后的类型依赖图:

           -> trait Bar<i32>    -> trait Foo (with i32 baked-in)
struct Baz
           -> trait Bar<String> -> trait Foo (with String baked-in)

我们最终得到Foo的2种不同实现:内置i32和内置String。 当我们编写<Baz as Foo>::new()时,编译器无法分辨出我们指的是Foo的哪个版本。他们是无法区分的。

经验法则是,仅当特征A在特征B的所有通用参数上通用时,特征A才能对特征B具有全面的实现。

相关问题