我遇到了无法在Box
es和其他通用结构中使用where子句的问题。这对于通用通信添加很有用。 (A + B = B + A)。我正在努力实现的一个例子:
enum Foo<T> {
Bar(Box<U where T: Add<U, Output = T>>),
Baz(Box<U where T: Sub<U, Output = T>>),
}
这可以使用类型定义中的where
子句半成品:
enum FooHack<T, A, S>
where
A: Add<T, Output = T>,
S: Sub<T, Output = T>,
{
Bar(A),
Baz(S),
Dummy(PhantomData<T>), // to stop the compiler from complaining
}
FooHack
的类型签名要求A
和S
是具体类型。是否有策略使A
和S
可以通用?
也许你已经知道trait objects了?这与你的目的完全匹配,让U
动态改变固定的T
。
但是,有两个问题阻止直接使用Add
或Sub
作为特征对象:
Add
和Sub
不是对象安全的,因为它期望一个按值的self
论证。Self
上是存在的,但你想要的特征对象存在于RHS
/ Add
的Sub
上。因此,您将不得不做额外的工作来介绍中间特征:
use std::ops::Add;
trait MyAdd<T> {
fn my_add(&self, other: T) -> T;
}
impl<T, U> MyAdd<T> for U
where
T: Add<U, Output = T>,
U: Clone,
{
fn my_add(&self, other: T) -> T {
other + self.clone()
}
}
enum Foo<T> {
Bar(Box<MyAdd<T>>),
}
如果您不想要U: Clone
,可以根据您的使用情况选择:
T: Add<&'a U, Output = T>
而不是T: Add<U, Output = T>
self: Box<Self>
中使用&self
而不是fn my_add
上面的代理特征MyAdd<T>
本身不代理运算符重载;因此你必须为此手动实现std::ops::Add
。
// Coherence error
impl<T> Add<Box<MyAdd<T>>> for T {
type Output = T;
fn add(self, other: Box<MyAdd<T>>) -> Self::Output {
other.my_add(self)
}
}
但是,由于一致性限制,此代码不起作用。这种限制是至关重要的,因为兄弟板条箱总是有可能像impl<T> Add<T> for TypeDefinedInTheSiblingCrate
一样,它肯定会与上面的impl重叠。
一种可能的解决方法是使用the newtype pattern:
struct Wrapper<T>(T);
impl<T> Add<Box<MyAdd<T>>> for Wrapper<T> {
type Output = Wrapper<T>;
fn add(self, other: Box<MyAdd<T>>) -> Self::Output {
Wrapper(other.my_add(self.0))
}
}
然后你可以写像Wrapper(x) + boxed_myadd_value + another_myadd_value
这样的东西。