可以在外国类型上实现特征吗?

Is possible to implement traits on foreign types?

我想实现向量与标量的频繁数学乘法:k * v = (k * v0, k * v1, k* v2..)。代码如下:

#[derive(Debug)]
struct V(Vec<i32>);

impl std::ops::Mul<V> for i32 {
    type Output = V;
    fn mul(self, v: V) -> V {
        V(v.0.iter().map(|v| v * self).collect())
    }
}

fn main() {
    let v = V(vec![1,2]);
    println!("{:?}", 3 * v);
}

这个解决方案有效,但是我想避免定义结构 V,而是编写类似这样的代码:

impl std::ops::Mul<Vec<i32>> for i32 {
    type Output = Vec<i32>;
    fn mul(self, v: Vec<i32>) -> Vec<i32> {
        v.iter().map(|v| v * self).collect()
    }
}

抛出以下错误:

 only traits defined in the current crate can be implemented for arbitrary types
  --> src/main.rs:45:1
   |
45 | impl std::ops::Mul<Vec<i32>> for i32 {
   | ^^^^^-----------------------^^^^^---
   | |    |                           |
   | |    |                           `i32` is not defined in the current crate
   | |    `Vec` is not defined in the current crate
   | impl doesn't use only types from inside the current crate
   |
   = note: define and implement a trait or new type instead

是否可以对外部类型使用乘法特征?

简而言之,没有。

您 运行 进入了“孤儿规则”。基本思想是,如果你想在 struct/enum/union Y 上实现一个特征 X,至少其中一个 必须定义在当前箱子。

这有时有点限制,但它是 Rust“一致性规则”的产物。通常,特征和类型的每个组合最多只能有 1 个实现。所以下面的代码是无效的:

struct Wrapper<T>(T);

trait Print {
  fn print(&self);
}

impl Print for Wrapper<i32> {
  fn print(&self) {
    println!("a very special int: {}", self);
  }
}

impl<T: Display> Print for Wrapper<T> {
  fn print(&self) {
    print!("a normal type: {}", self);
  }
}

假设您在代码中编写 0i32.print()。 rustc 选择哪种实现?答案不明确(在 pre-specialization 世界中),因此鉴于 Rust 的显式原则,代码被彻底拒绝。

孤立规则是一种通常使生态系统更有用的机制。打破孤儿规则(如果可能的话)本身并没有什么不妥,但是,它会导致一个不幸的场景,你可能会使用 2 个 crate,它们为某些外部类型和特征定义了自己的实现。

想象一下,例如,一个板条箱决定添加这个(看起来相当合理的)实现:

impl<T> Display for Vec<T> where T: Display {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    todo!()
  }
}

这很好,直到您要导入另一个 crate 也定义了类似的 impl。即使代码相同,它们也将是不同的实现,因此会不连贯并且无法编译。

孤立规则保护生态系统免受此类问题的影响。

包装器在 Rust 中是惯用的,不会强加运行时成本。如果你发现自己写了很多东西,我已经写了一个库来自动化很多样板文件,叫做 microtype 这可能很有用。