[英]Why can't I add a blanket impl on a trait with a type parameter?
Consider these two traits:考虑这两个特征:
pub trait Foo {
fn new(arg: u32) -> Self;
}
pub trait Bar<P>: Foo {
fn with_parameter(arg: u32, parameter: P) -> Self;
}
I'd like to add the blanket impl:我想添加一揽子实现:
impl<T: Bar<P>, P: Default> Foo for T {
fn new(arg: u32) -> Self {
Self::with_parameter(arg, P::default())
}
}
But I get the compiler error:但我收到编译器错误:
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
I think I get this error because I'm violating trait coherence rules, but I don't understand exactly what rule this would break.我想我收到这个错误是因为我违反了特征一致性规则,但我不明白这会破坏什么规则。 Why is this pattern not allowed?
为什么不允许这种模式? And, more importantly, can I achieve what I want without getting an error?
而且,更重要的是,我可以在不出错的情况下实现我想要的吗?
The problem is that a single type could implement Bar<P>
for multiple values of P
.的问题是,单一类型的可以实现
Bar<P>
为的多个值P
。 If you had a struct Baz
that implemented Bar<i32>
and Bar<String>
, which type should Foo::new
use for P
?如果你有一个结构体
Baz
实现了Bar<i32>
和Bar<String>
,那么Foo::new
应该为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!).唯一的解决方案是确保单个类型不能多次实现
Bar
(如果这不是您想要的,那么您的设计就有缺陷!)。 To do so, we must replace the P
type parameter with an associated type.为此,我们必须用关联类型替换
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())
}
}
An implementation of Bar
would look like this: Bar
的实现如下所示:
struct Baz;
impl Bar for Baz {
type Parameter = i32;
fn with_parameter(arg: u32, parameter: Self::Parameter) -> Self {
unimplemented!()
}
}
See also:也可以看看:
I've broken down and extended Francis's explanation of why the code does not compile.我已经分解并扩展了Francis对代码无法编译的原因的解释。 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
:让我们创建
Baz
,它在 2 个变体中实现Bar
: i32
和String
:
struct Baz;
impl Bar<i32> for Baz { /* ... */ }
impl Bar<String> for Baz { /* ... */ }
Type dependency graph after blanket impl takes effect:覆盖 impl 生效后的类型依赖图:
-> 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
.我们最终得到了
Foo
两种不同实现:内置i32
和内置String
。 When we write <Baz as Foo>::new()
, compiler can't tell which version of Foo
we mean;当我们将
<Baz as Foo>::new()
,编译器无法分辨我们指的是哪个版本的Foo
; 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.经验法则是,只有当 trait A 对 trait B 的所有泛型参数是通用的时,trait A 才能对 trait B 进行全面实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.