为什么我不能在带有类型参数的特征上添加一揽子实现?
Why can't I add a blanket impl on a trait with a type parameter?
考虑这两个特征:
pub trait Foo {
fn new(arg: u32) -> Self;
}
pub trait Bar<P>: Foo {
fn with_parameter(arg: u32, parameter: P) -> Self;
}
我想添加毛毯实施例:
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
我想我收到这个错误是因为我违反了特征一致性规则,但我不明白这会违反什么规则。为什么不允许这种模式?而且,更重要的是,我能否在不出错的情况下实现我想要的目标?
问题是单个类型可以为 P
的多个值实现 Bar<P>
。如果您有一个实现了 Bar<i32>
和 Bar<String>
的结构 Baz
,Foo::new
应该为 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
的实现如下所示:
struct Baz;
impl Bar for Baz {
type Parameter = i32;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self {
unimplemented!()
}
}
另请参阅:
我已经分解并扩展了 代码无法编译的原因。我可能不是这个街区最聪明的孩子,但我花了很长时间才理解他简洁的推理。
让我们创建 Baz
,它以 2 个变体实现 Bar
:i32
和 String
:
struct Baz;
impl Bar<i32> for Baz { /* ... */ }
impl Bar<String> for Baz { /* ... */ }
blanket impl 生效后的类型依赖图:
-> trait Bar<i32> -> trait Foo (with i32 baked-in)
struct Baz
-> trait Bar<String> -> trait Foo (with String baked-in)
我们最终得到 Foo
的 2 种不同实现:baked-in i32
和 baked-in String
。
当我们写 <Baz as Foo>::new()
时,编译器无法判断我们指的是哪个版本的 Foo
;他们是无法区分的。
经验法则是,只有当特征 A 对特征 B 的所有通用参数具有通用性时,特征 A 才能对特征 B 进行全面实施。
考虑这两个特征:
pub trait Foo {
fn new(arg: u32) -> Self;
}
pub trait Bar<P>: Foo {
fn with_parameter(arg: u32, parameter: P) -> Self;
}
我想添加毛毯实施例:
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
我想我收到这个错误是因为我违反了特征一致性规则,但我不明白这会违反什么规则。为什么不允许这种模式?而且,更重要的是,我能否在不出错的情况下实现我想要的目标?
问题是单个类型可以为 P
的多个值实现 Bar<P>
。如果您有一个实现了 Bar<i32>
和 Bar<String>
的结构 Baz
,Foo::new
应该为 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
的实现如下所示:
struct Baz;
impl Bar for Baz {
type Parameter = i32;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self {
unimplemented!()
}
}
另请参阅:
我已经分解并扩展了
让我们创建 Baz
,它以 2 个变体实现 Bar
:i32
和 String
:
struct Baz;
impl Bar<i32> for Baz { /* ... */ }
impl Bar<String> for Baz { /* ... */ }
blanket impl 生效后的类型依赖图:
-> trait Bar<i32> -> trait Foo (with i32 baked-in)
struct Baz
-> trait Bar<String> -> trait Foo (with String baked-in)
我们最终得到 Foo
的 2 种不同实现:baked-in i32
和 baked-in String
。
当我们写 <Baz as Foo>::new()
时,编译器无法判断我们指的是哪个版本的 Foo
;他们是无法区分的。
经验法则是,只有当特征 A 对特征 B 的所有通用参数具有通用性时,特征 A 才能对特征 B 进行全面实施。