根据实际的最终目标,那么通过使用辅助特性和blanket implementation来做你正在做的事情会更容易.
简而言之,与其这样做:
trait Foo {
fn foo(&self) {
println!("default foo");
}
}
impl Foo for String {}
然后你引入一个trait HasFoo
并使用它触发一个blanket implementation的Foo
.看起来像这样:
trait Foo {
fn foo(&self);
}
trait HasFoo {}
impl<T> Foo for T
where
T: HasFoo,
{
fn foo(&self) {
println!("default foo");
}
}
impl HasFoo for String {}
我们可以对您的示例执行相同的操作,通过引入trait HasAB
来触发A
和B
的blanket implementation:
(Rust Playground)
trait HasAB {}
trait A {
fn a(&self, count: i32) -> String;
}
trait B {
fn b(&self, count: i32) -> String;
}
impl<T> A for T
where
T: HasAB + Display,
{
fn a(&self, count: i32) -> String {
if count == 0 {
String::new()
} else {
format!("[{}]a{}", self, self.b(count - 1))
}
}
}
impl<T> B for T
where
T: HasAB + Display,
{
fn b(&self, count: i32) -> String {
if count == 0 {
String::new()
} else {
format!("[{}]b{}", self, self.a(count - 1))
}
}
}
现在你只需要执行以下操作,然后你的示例编译:
impl HasAB for String {}
此外,由于我们取消了A: B
和B: A
的界限.然后,你现在可以为不实现B
的类型使用impl A
,反之亦然.你以前做不到.
您仍然可以手动实现每个特征.不强制为所有类型实现所有它们.
否则,当你在处理违约问题时.然后,将代码移到不同的函数中是很常见的.就像这样:
impl A for Foo {
fn a(&self, count: i32) -> String {
default_a_where_b(self, count)
}
}
fn default_a_where_b<T>(t: &T, count: i32) -> String
where
T: B + Display,
{
if count == 0 {
String::new()
} else {
format!("[{}]a{}", t, t.b(count - 1))
}
}
如果您想摆脱样板,那么您可以引入一个宏:
macro_rules! impl_A_where_B {
($t:ty) => {
impl A for $t {
fn a(&self, count: i32) -> String {
default_a_where_b(self, count)
}
}
};
}
and vice versa for 100.
现在你只需做:
impl_A_where_B!(Foo);
由于我们really没有specialization也没有negative trait bound,所以在我们想要默认实现和双向边界A: B
和B: A
的情况下,它就更难兼顾这些情况.
如果是因为您希望同时实现这两个特征,那么您可以改为在函数上指定该界限.然后,您可以避免循环错误:
fn f<T>(t: &T) where T: A + B {}
总的来说,Rust并没有真正玩得很好(还?)你要做的事在现实中,你可能会更好地 Select 一个完全独立的设计.然而,如果没有更具体的例子,很难提出替代解决方案.