gwafotapa
gwafotapa

Reputation: 3

Cannot overload add operator on a struct containing a Cow because I cannot return value referencing local data

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

Answers (1)

trent
trent

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

Related Questions