为什么在这里使用sum()需要更高级别的特征界?



我在Rust中写一个通用矩阵库,想做一个点积函数

:

pub fn dot<'a, R: Scalar>(&'a self, rhs: &'a Matrix<R, M, 1>) -> T
where
T: Sum<&'a T>,
&'a Self: Mul<&'a Vector<R, M>, Output = Self>,
{
(self * rhs).elements().sum::<T>()
}

引起了一个令人困惑的错误,我不理解:

304 |     pub fn dot<'a, R: Scalar>(&'a self, rhs: &'a Matrix<R, M, 1>) -> T
|                -- lifetime `'a` defined here
...
309 |         (self * rhs).elements().sum::<T>()
|         ^^^^^^^^^^^^-----------
|         |
|         creates a temporary which is freed while still in use
|         argument requires that borrow lasts for `'a`
310 |     }
|     - temporary value is freed at the end of this statement

在寻找相似的问题盲目地尝试不同的东西之后,我找到了

的解决方案
pub fn dot<'a, R: Scalar>(&'a self, rhs: &'a Matrix<R, M, 1>) -> T
where
T: for<'b> Sum<&'b T>,
&'a Self: Mul<&'a Vector<R, M>, Output = Self>,
{
(self * rhs).elements().sum::<T>()
}

这个可以,但是我不明白为什么需要。为什么临时不持续整个功能块的长度?'b所指的寿命是多少?

背景:

Matrix

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Matrix<T, const M: usize, const N: usize>
where
T: Scalar,
{
data: [[T; N]; M], // Column-Major order
}

Vector

pub type Vector<T, const N: usize> = Matrix<T, N, 1>;

elements()返回data上的扁平迭代器。mul返回与Self相同的类型。如果我将dot()主体更改为:

,我会得到相同的错误self.clone().data[0].iter().sum::<T>()

所以这不是Matrix上的其他方法的问题

如果我将self * rhs赋值给变量 ,同样的事情也会发生

问题是您使用了太多的生命周期注释。如果你真的考虑你给编译器的生命周期,这是对的。您正在放弃临时的,而它仍在使用中。对于绑定的T: Sum<&'a T>,您已经告诉它T可以由&'a Ts制成。'aself寿命相同。:&Self * &Vector<R, M>返回一个新的拥有的Self,称其为P,它只存在于函数剩余的时间,称其为'p

现在,elements可能有一个与fn elements<'a>(&'a self) -> impl Iterator<Item = &'a T>相同的签名(可能其中一些生命周期实际上被省略了,但它们仍然存在)。所以当你调用P.elements时,你实际上调用的是elements(&'p P) -> impl Iterator<Item = &'p T>

那么,当你调用sum::<T>时,编译器只知道如何从&'a Ts中sum一个新的T,所以它推断'p一定等于'a。但它们显然不是。'p只持续到函数结束,而'a继续存活。这就是为什么你会得到这个错误:你已经绑定了T,你返回的self的生命周期没有任何原因。

一个经验法则,我没有想太多,但可能仍然是有用的是:如果你借用self不可变和返回类型不捕获生命周期(即不是一个引用,一个trait对象,一个impl Trait,或调用者提供的泛型),你可能不需要指定参数的生命周期。

也就是说:你不关心selfrhs的生命周期,因为你返回的是一个不引用它们的拥有类型。

当然,在where子句中仍然需要生命周期,这就是for<'x> _: ...的由来。这被称为HRTB,它的意思是,"在我插入'x的任何生命周期内,:之后的内容都是有效的。">这就是你所关心的。只要你只有引用,它就可以工作,而不是只要你有任何特定'x的引用。

所以你可以简单地重写dot的签名,像这样

pub fn dot<R: Scalar>(&self, rhs: &Matrix<R, M, 1>) -> T
where
for<'x> T: Sum<&'x T>,
for<'y> &'y Self: Mul<&'y Vector<R, M>, Output = Self>,
{ // ...

最新更新