3

我正在尝试将 nalgebra 和/或 ndarray Rust crates 包装成一个抽象LinearOperator特征和相应的AdjointableOperator等特征。问题是我得到了显着的“特征绑定膨胀”,甚至必须指定内部实现细节(Storage, DefaultAllocator)以使编译器不阻塞。在代数的情况下:

impl<SM,SV,N,M,K,E> LinearOperator<Matrix<E,M,K,SV>> for Matrix<E,N,M,SM>
where SM: Storage<E,N,M>, SV: Storage<E,M,K>,
      N : Dim, M : Dim, K : Dim, E : Scalar + ClosedMul + ClosedAdd + Zero + One,
      DefaultAllocator : Allocator<E,N,K>,
      DefaultAllocator : Allocator<E,M,K>,
      DefaultAllocator : Allocator<E,N,M>,
      DefaultAllocator : Allocator<E,M,N> {
    type Codomain = OMatrix<E,N,K>;
    fn apply(&self, x : &Matrix<E,M,K,SV>) -> Self::Codomain {
        self.mul(x)
    }
}

(在向量到向量操作的情况下,维度K将为 1。理想情况下apply只是一个函数调用,但编译器目前不允许定义它。)其中一些可能不是单独需要的LinearOperator(例如DefaultAllocator : Allocator<E,M,N>,对于伴随维度),但编译器甚至开始要求它们LinearOperator一次AdjointableOperator也被定义。在 ndarray 的情况下,边界的行数较少,但仍需要指定几乎没有记录的半内部特征。

type GM<S> = ArrayBase<S,Ix2>;
type GV<S> = ArrayBase<S,Ix1>;

impl<SM,SV,E> LinearOperator<GV<SV>> for GM<SM>
where SM: ArrayData<Elem=E>, SV: ArrayData<Elem=E>, E : LinalgScalar {
    type Codomain = Array1<E>;
    fn apply(&self, x : &GV<SV>) -> Array1<E> {
        self.dot(x)
    }
}

所有这些 trait bound 都需要为 theAdjointableOperator和其他 trait 重复,所以我尝试至少将需求压缩到自定义 trait 中:

trait ValidMatrix<E,N,M,SM>
where N : Dim, M : Dim, E : Scalar + ClosedMul + ClosedAdd + Zero + One,
      DefaultAllocator : Allocator<E,N,M>, SM: Storage<E,N,M> {
}

impl<E,N,M,SM> ValidMatrix<E,N,M,SM> for Matrix<E,N,M,SM>
where N : Dim, M : Dim, E : Scalar + ClosedMul + ClosedAdd + Zero + One,
      DefaultAllocator : Allocator<E,N,M>, SM: Storage<E,N,M> {
}

然后仅Matrix<E,N,M,SM> : ValidMatrix<E,N,M,SM>在 的定义中指定LinearOperator。编译器立即开始抱怨DefaultAllocator(可能)未实现。

那么有什么办法可以避免这种“trait bound bloat”,即 crate 的用户基本上必须访问越来越多的 crate 内部实现细节来为暴露的类型实现自己的特征?我可以考虑mul在特征中指定等ValidMatrix,并实现这些,但首先以这种方式设计上游板条箱肯定会更有效。

4

0 回答 0