我一直在研究多维数组库,尝试使用不同的接口(interface),遇到了一个我似乎无法解决的问题。这可能是对生命的一个简单误解,但我已经尝试了几乎所有我能想到的解决方案,但都没有成功。
目标:实现 Index 和 IndexMut 特征以从二维矩阵返回借用的向量,因此可以使用此语法 mat[rowind][colind]
。
数据结构定义的(非常简化的)版本如下。
pub struct Matrix<T> {
shape: [uint, ..2],
dat: Vec<T>
}
impl<T: FromPrimitive+Clone> Matrix<T> {
pub fn new(shape: [uint, ..2]) -> Matrix<T> {
let size = shape.iter().fold(1, |a, &b| { a * b});
// println!("Creating MD array of size: {} and shape: {}", size, shape)
Matrix{
shape: shape,
dat: Vec::<T>::from_elem(size, FromPrimitive::from_uint(0u).expect("0 must be convertible to parameter type"))
}
}
pub fn mut_index(&mut self, index: uint) -> &mut [T] {
let base = index*self.shape[1];
self.dat.mut_slice(base, base + self.shape[1])
}
}
fn main(){
let mut m = Matrix::<f32>::new([4u,4]);
println!("{}", m.dat)
println!("{}", m.mut_index(3)[0])
}
mut_index 方法的工作原理与我希望 IndexMut 特性的工作原理完全相同,当然它没有语法糖。实现 IndexMut 的第一次尝试让我感到奇怪,因为它返回了对指定类型的借用引用,我真的很想将 [T]
指定为一个类型,但它不是一个有效的类型。所以唯一的选择是像这样指定 &mut [T]
。
impl<T: FromPrimitive+Clone> IndexMut<uint, &mut [T]> for Matrix<T> {
fn index_mut(&mut self, index: &uint) -> &mut(&mut[T]) {
let base = index*self.shape[1];
&mut self.dat.mut_slice(base, base + self.shape[1])
}
}
这会提示 trait impl 行中缺少生命周期说明符。所以我尝试添加一个。
impl<'a, T: FromPrimitive+Clone> IndexMut<uint, &'a mut [T]> for Matrix<T> {
fn index_mut(&'a mut self, index: &uint) -> &mut(&'a mut[T]) {
let base = index*self.shape[1];
&mut self.dat.mut_slice(base, base + self.shape[1])
}
}
现在我得到 method `index_mut` has an incompatible type for trait: expected concrete lifetime, but found bound lifetime parameter 'a [E0053]
。除此之外,我尝试了几乎所有我能想到的一个和两个生命周期的组合,并创建了一个二级结构来保存在索引操作期间存储在外部结构中的引用,因此对 的引用that 可以返回,但对于 Index 来说这是不可能的。鉴于对这个旧 github issue 的回应,最终的答案可能只是这是不可能的。 ,但这似乎是 Index 和 IndexMut 特征的一个有问题的限制。有什么我想念的吗?
最佳答案
目前,这是不可能的,但当动态大小类型落地时,我相信这将成为可能。
我们来看the signature :
pub trait IndexMut<Index, Result> {
fn index_mut<'a>(&'a mut self, index: &Index) -> &'a mut Result;
}
(请注意,与文档所说相比,添加了 <'a>
;我已经提交了 #16228。)
'a
是任意生命周期,但重要的是它在方法 上指定,而不是在整个 impl 上指定:它绝对是方法的通用参数。我将用名称 'ρ₀
来展示这一切是如何产生的和 'ρ₁
.那么,在这次尝试中:
impl<'ρ₀, T: FromPrimitive + Clone> IndexMut<uint, &'ρ₀ mut [T]> for Matrix<T> {
fn index_mut<'ρ₁>(&'ρ₁ mut self, index: &uint) -> &'ρ₁ mut &'ρ₀ mut [T] {
let base = index * self.shape[1];
&mut self.dat.mut_slice(base, base + self.shape[1])
}
}
这满足以下要求:(a) 所有生命周期必须在 impl
中显式显示 header ,以及 (b) 方法签名与特征定义匹配:Index
是uint
和 Result
是&'ρ₀ mut [T]
.因为'ρ₀
在 impl block 上定义(以便它可以用作那里的参数)和 'ρ₁
在方法上(因为这是特征定义的),'ρ₀
和 'ρ₁
不能组合成一个命名的生命周期。 (你可以将它们都称为 'a
,但这是阴影,除了引入更多的困惑之外不会改变任何东西!)
然而,这还不足以让它全部工作,它确实不会编译,因为 'ρ₀
没有绑定(bind)到任何东西,也没有绑定(bind)到签名中。所以你不能投 self.data.mut_slice(…)
, 类型为 &'ρ₁ mut [T]
, 至 &'ρ₀ mut [T]
因为生命周期不匹配,它们之间也没有任何已知的子类型关系(也就是说,无法在结构上证明生命周期 'ρ₀
小于— 'ρ₁
的子类型;尽管方法的返回类型会明确这一点,但在基本类型级别上并非如此,因此是不允许的)。
碰巧,IndexMut
由于#12825,它并没有像它应该的那样有用, 作为 matrix[1]
总是会使用 IndexMut
永远不会Index
如果您同时实现了两者。不过,我不确定这是否是一种安慰!
解决方案来自动态大小类型。当它在这里时,[T]
将是合法的未调整大小的类型,可用作 Result
的类型所以这将是这样写的:
impl<T: FromPrimitive + Clone> IndexMut<uint, [T]> for Matrix<T> {
fn index_mut<'a>(&'a mut self, index: &uint) -> &'a mut [T] {
let base = index * self.shape[1];
&mut self.dat.mut_slice(base, base + self.shape[1])
}
}
……但这还没有。
关于rust - Index 和 IndexMut 实现返回借用的向量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25106355/