Reputation: 3
I'm implementing basic operations for matrices on a generic field F
. In doing so, I want to overload the add operator for the matrix type. The matrix type contains a Cow
either owning or borrowing F
. I want the resulting matrix sum to borrow the F
of the added matrix regardless if it owns or borrows F
.
The compiler complains that I return a reference created inside the function, but I don't understand why. It seems to me I'm referencing an input argument of the function and not data created locally. Here's my minimal example:
use std::{
borrow::{Borrow, Cow},
ops::Add,
};
pub trait Field {
type FElt: Copy + Eq;
fn zero(&self) -> Self::FElt;
fn add(&self, a: Self::FElt, b: Self::FElt) -> Self::FElt;
}
pub struct Mat<'a, F: Clone + Eq + Field> {
field: Cow<'a, F>,
rows: usize,
cols: usize,
data: Vec<F::FElt>,
}
impl<'a, F: Clone + Eq + Field> Mat<'a, F> {
pub fn zero(field: &'a F, rows: usize, cols: usize) -> Self {
if rows == 0 || cols == 0 {
panic!("Empty matrix");
}
Self {
field: Cow::Borrowed(field),
rows,
cols,
data: vec![field.zero(); rows * cols],
}
}
}
impl<'a, F: Clone + Eq + Field> Add for Mat<'a, F> {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
if self.field != other.field {
panic!("Cannot add matrices: fields do not match");
}
if self.rows != other.rows || self.cols != other.cols {
panic!("Cannot add matrices: dimensions do not match");
}
let f: &F = self.field.borrow();
let mut sum = Mat::zero(f, self.rows, self.cols);
for i in 0..self.rows * self.cols {
sum.data[i] = f.add(self.data[i], other.data[i]);
}
sum
}
}
fn main() {}
Here's the error I'm getting:
error[E0515]: cannot return value referencing local data `self.field`
--> src/main.rs:51:9
|
46 | let f: &F = self.field.borrow();
| ---------- `self.field` is borrowed here
...
51 | sum
| ^^^ returns a value referencing data owned by the current function
Upvotes: 0
Views: 124
Reputation: 28005
It seems to me I'm referencing an input argument of the function and not data created locally.
self
may not have been created locally, but it is moved into the add
method (which takes self
), so it will still be destroyed when the function returns. If the output were allowed to reference it, the reference would become dangling as soon as the function returned.
If you want the output to borrow from the input, you must take the input as a reference, which means implementing Add
for &'b Mat<'a, F>
instead of Mat<'a, F>
. See How do I implement the Add trait for a reference to a struct? Note that the Output
type must be Mat<'b, F>
instead of Mat<'a, F>
.
There's another, simpler solution for the problem as posed. Borrowing doesn't work because the Cow
was mooved in to the function. Why not just move it back out? An easy way to do this is to modify self
and return it rather than creating sum
inside the function.
fn add(mut self, other: Self) -> Self::Output {
/* ... */
for i in 0..self.rows * self.cols {
self.data[i] = self.field.add(self.data[i], other.data[i]);
}
self
}
Not only do you avoid a potentially expensive clone of field
, but you also reuse data
's buffer this way.
Upvotes: 1