使用泛型的特征的毯子实现

use*_*064 6 generics matrix rust

我正在通过实现矩阵数学来练习Rust,而且我遇到了一些障碍.我定义了我认为与矩阵相关的特征.

trait Matrix<T> where T : num::Num  {
    fn dim(&self) -> (usize, usize);
    fn elem(&self, i : usize, j : usize) -> Option<& T>; 
    fn new_const(v: T, rows : usize, cols : usize) -> Self where T : Clone;

    fn same_dim<U>(&self, other: &U) -> bool where U : Matrix<T> {
        self.dim() == other.dim()
    }
}
Run Code Online (Sandbox Code Playgroud)

我有一个愚蠢的实现使用Vec<Vec<T>>.我实现了所有方法并测试了它们.他们都在工作.现在我想简单地将两个矩阵一起添加.因此,如果不添加我知道将需要的行迭代器并执行我知道的添加实现将是不正确的,我将以下内容添加.

impl <T, U> Add for U where T: num::Num, U: Matrix<T> {
    type Output = U;

    fn add(self, _rhs: U) -> U {
        U::new_const(T::zero(), 5, 5)
    }
}
Run Code Online (Sandbox Code Playgroud)

但我明白了

lib.rs:41:7: 41:8 error: the type parameter `T` is not constrained by the impl trait, self type, or predicates [E0207]
lib.rs:41 impl <T, U> Add for U where T: num::Num, U: Matrix<T> {
                ^
lib.rs:41:7: 41:8 help: run `rustc --explain E0207` to see a detailed explanation
error: aborting due to previous error
Could not compile `matrix`.
To learn more, run the command again with --verbose.
Compilation failed.
Run Code Online (Sandbox Code Playgroud)

T似乎对我有用.谁能指出我正确的方向?

huo*_*uon 8

T不受编译器在实现的使用站点上看到的信息的约束.通过使用trait Matrix<T>,单个类型可以是多个元素类型的矩阵,也就是说,对于某个人来说,拥有这两个元素impl Matrix<u8> for Foo并且impl Matrix<u16> for Foo在同一个程序中是完全合法的.如果发生这种情况,那么Foo + Foo(即使用的Add实现)无法确定其T使用方法:既T = u8T = u16工作.

我认为解决这个问题的最好方法是删除T类型参数:给定类型只是一种元素上的矩阵(即使它是通用的),例如Vec<Vec<T>>,矩阵结束T,没有别的.这就像Iterator:给定类型只能产生一种类型的元素.

在代码中,这看起来像:

trait Matrix {
    type Elem: num::Num;

    fn dim(&self) -> (usize, usize);
    fn elem(&self, i: usize, j: usize) -> Option<&Self::Elem>;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

然后Add实现变为

impl<U> Add for U where U: Matrix {
    type Output = U;

    fn add(self, _rhs: U) -> U {
        U::new_const(U::Elem::zero(), 5, 5)
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这也不起作用,因为它会遇到连贯性问题.编译器无法确定此实现Add是否与其他实现不重叠Add,因为可以以某种方式实现Matrix已经具有Add实现的类型.可以通过制作包装类型来解决这个问题,并将该Matrix特征用作"后备存储"特征,即它"仅"控制内部表示.

struct Matrix<T: Storage> {
    x: T
}
trait Storage { // renamed `Matrix` trait
    type Elem;
    fn dim(&self) -> (usize, usize);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

Matrix然后该类型成为添加更多方法和特征实现等的地方.