Im writing a generic matrix library in Rust, and wanted to make a dot product function
this:
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>()
}
caused a confusing error I didn't understand:
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
After searching for similar issues blindly trying different things, I came to the solution of
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>()
}
this works, but I don't understand why its necessary. Why is the temporary not lasting the length of the entire function block? and what is the lifetime that 'b is referring to?
Context:
Matrix is
#[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
}
and Vector is
pub type Vector<T, const N: usize> = Matrix<T, N, 1>;
elements() returns a flattened iterator over data. mul returns the same type as Self. I get the same error if I change the dot() body to:
self.clone().data[0].iter().sum::<T>()
so it isn't an issue with other methods on Matrix
the same thing also happens if I assign self * rhs to a variable
The problem is that you're using too many lifetime annotations as is. If you actually think about the lifetimes you've given the compiler, it's right. You are dropping the temporary while it's still in use. With the bound
T: Sum<&'a T>you've told it that aTcan be made from&'a Ts. And'ais the same lifetime for whichselfis borrowed. But:&Self * &Vector<R, M>returns a new, ownedSelf, call itP, that lives only for the remainder of the function, call that lifetime'p.Now,
elementsprobably has a signature along the lines offn elements<'a>(&'a self) -> impl Iterator<Item = &'a T>(maybe some of those lifetimes are actually elided, but they're still there). So when you callP.elements, you're calling, essentially,elements(&'p P) -> impl Iterator<Item = &'p T>.Then, when you call
sum::<T>on that, the compiler only knows how tosuma newTfrom&'a Ts, so it infers that'pmust be equal to'a. But they're clearly not.'ponly lasts to the end of the function while'akeeps on living. That's why you're getting the error: You've tied theTyou're returning to the lifetime ofselffor no reason.A rule of thumb I've not thought too hard about but might still be useful is: if you borrow
selfimmutably and the return type doesn't capture a lifetime (i.e. isn't a reference, a trait object, animpl Trait, or a caller supplied generic), you probably don't need to specify the arguments' lifetimes.That is to say: you don't care about the lifetimes of
selforrhsbecause you're returning an owned type that doesn't reference either of them.Of course, you'll still need lifetimes in your
whereclause and that's wherefor<'x> _: ...comes in. That's called a HRTB and basically says, "for any lifetime I plug into'xwhat comes after the:will be valid." That's what you care about. That it works as long as you only have references, not as long as you have references that live for any particular'x.So you can simply re-write
dot's signature like this