解决特质实现冲突

7

我正在尝试在Rust中编写一些通用的数学函数,但我一直遇到以下错误信息:

error: conflicting implementations for trait SoAndSo

是否可能解决这个问题?如果可以,如何解决?

例如,我试图编写一个通用的点积函数,它接受两个迭代器,将它们压缩,并迭代每对元素以累加乘积。我希望这个函数也能够计算复数点积。复数点积涉及共轭左侧一项。我的第一个想法是编写一个名为Dot1的特质,用于替换Mul,使其也共轭左侧参数。以下是完整代码:

extern crate num;

use num::complex::Complex;
use num::{Float, Num};

trait Dot1<Rhs, Result> {
    fn dot1(&self, rhs: Rhs) -> Result;
}

impl<T: Float> Dot1<T, T> for T {
    // conjugation for reals is a no-op
    fn dot1(&self, rhs: T) -> T {
        *self * rhs
    }
}

impl<T: Num + Clone> Dot1<Complex<T>, Complex<T>> for Complex<T> {
    fn dot1(&self, rhs: Complex<T>) -> Complex<T> {
        self.conj() * rhs
    }
}

fn main() {
    println!("Hello, world!")
}

由于Complex<T>不是Float,因此两者之间不应有重叠,“通用实现”也应如此。我并未预料到任何问题,但每次我尝试为特质提供多个“通用实现”时,编译器都不喜欢它:

error[E0119]: conflicting implementations of trait `Dot1<num::Complex<_>, num::Complex<_>>` for type `num::Complex<_>`:
  --> src/main.rs:17:1
   |
10 | impl<T: Float> Dot1<T, T> for T {
   | ------------------------------- first implementation here
...
17 | impl<T: Num + Clone> Dot1<Complex<T>, Complex<T>> for Complex<T> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `num::Complex<_>`
   |
   = note: upstream crates may add new impl of trait `num::Float` for type `num::Complex<_>` in future versions

我如何编写一个通用的、基于迭代器的点积算法,既适用于实数也适用于复数?取出迭代器、压缩它们等操作都不是问题,我甚至能够确定哪些类型参数与哪些边界一起使用。但我似乎无法使用像上面那样的特性“统一”某些数据类型。


这将在实施PR 48时变为可能。 - Arjan
1个回答

3
问题在于(当前的)编译器没有注册Complex<T>没有实现Float。想象一下,如果Complex实现了Float:要么必须有某种方法来决定使用哪个impl,要么重叠的代码必须被禁止……但是,如果有人只是在另一个编译器不知道的箱子中添加了impl<T> Float for Complex<T>呢?
这个最后一点很关键:与 Haskell 不同,Rust 的设计允许此代码完全没有任何风险,这是因为 Rust 在处理最后一点时有所不同。如果您在一个能看到类型 Type 和 trait Trait 的 crate 中,那么您可以百分之百确定 Type 是否实现了 Trait:Rust 没有 Haskell 的开放世界假设,因为只有当 TraitType 位于当前 crate(编译单元)中时,才能编写 impl Trait for Type,也就是说,您不能有 孤儿实例,即在某个远程 crate 中,有人为 Complex<T> 实现 Float

RFC 24(Chris 提供的链接)认可此点,允许这些通用实现与更具体的实现共存,只要保证这些实现不会重叠。


Complex<T>T“更专业”,这不应该有所考虑吗?抱歉,再问一遍,放弃这种包括类似C ++的重载分辨率的行为的原因是什么?请帮助一个C ++程序员理解Rust的设计选择。 - sellibitze
目前 Rust 还没有对特化进行识别。例如,你需要有一种方法来解决在 (uint, uint) 中使用 impl<T> Trait for (uint, T)impl<T> Trait for (T, uint) 的问题。从哲学上讲,Haskell 的系统是类似的,你需要通过语言扩展显式地选择这些类型的 impl,然而,那里的主要问题不一定适用于 Rust(与本答案中描述的相同)。某种形式的 impl 特异性可能会在(长期的)未来添加进来。 - huon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接