我正在尝试将 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
,并实现这些,但首先以这种方式设计上游板条箱肯定会更有效。