Referencing when overloading operators on structs

Why isn't this working? I assume it has to do with the fact that I'm taking two Box<[&T]> and outputting a Box<[T]>, but wouldn't it automatically perform dereferencing if T has the Copy trait?

use std::ops::Add;

fn main() {
    let a = Matrix::new(&[2, 1, -2, 4], 2);
    let b = Matrix::new(&[-1, -3, 0, 2], 2);
    println!("{:?}", a + b);
}

#[derive(Debug)]
pub struct Matrix<T> {
    data: Box<[T]>,
    width: usize,
}

impl<T> Matrix<T> {
    pub fn new(data: impl IntoIterator<Item = T>, width: usize) -> Self {
        let data: Box<[T]> = data.into_iter().collect();
        assert!(data.len() % width == 0);
        Matrix { data, width }
    }
}

impl<'a, 'b, T: Add<Output = T> + Copy> Add<&'b Matrix<T>> for &'a Matrix<T> {
    type Output = Matrix<T>;
    fn add(self, rhs: &'b Matrix<T>) -> Matrix<T> {
        Matrix::new(
            self.data
                .into_iter()
                .zip(rhs.data.into_iter())
                .map(|(u, v)| *u + *v),
            self.width,
        )
    }
}

but wouldn't it automatically perform dereferencing if T has the Copy trait?

Rust doesn't automatically dereference. Also, automatic reference won't be applied for operator arguments.
The standard library implements operators for reference types e.g. &i32 + &i32, &i32 + i32 and i32 + &i32 in addition to i32 + i32, but this is done by manual implementations (actually, macro is used).
Because you defined only &Matrix + &Matrix, arguments of the plus operator have to be referenced:

    println!("{:?}", &a + &b);

However, the code still doesn't compile. This is because the type of a and b is Matrix<&i32>. You are passing &[i32; 4] to Matrix::new. It is a IntoIterator but with Item = &i32. This issue can be solved by copying iterator elements:

    let a = Matrix::new([2, 1, -2, 4].iter().copied(), 2);
    let b = Matrix::new([2, 1, -2, 4].iter().copied(), 2);

So the implementation of Add for &Matrix would look like

impl<'a, 'b, T: Add<Output = T> + Copy> Add<&'a Matrix<&'a T>> for &'a Matrix<& 'a T> {
    type Output = Matrix<T>;
    fn add(self, rhs: &Matrix<&T>) -> Self::Output {
        Matrix::new(
            self.data
                .into_iter()
                .zip(rhs.data.iter())
                .map(|(u, v)| **u + **v),
            self.width,
        )
    }
}

and for owned

impl<'a, 'b, T: Add<Output = T> + Copy> Add<Matrix<&'a T>> for Matrix<& 'a T> {
    type Output = Matrix<T>;
    fn add(self, rhs: Matrix<&T>) -> Self::Output {
        Matrix::new(
            self.data
                .into_iter()
                .zip(rhs.data.iter())
                .map(|(u, v)| **u + **v),
            self.width,
        )
    }
}

Wouldn't there be a large memory overhead when trying to add large matrices then?

Probably you are misunderstanding about reference and owned values.
Matrix<&i32> means a matrix of references (a pointer). Which is likely not what you want.
Values being referenced has to be owned by someone. The result of addition has to be an owned value.

This is not completely true.

Consider this code:

fn blah(a: &str) {
    // nop
} 

fn main() {
    let foo = String::from("foo");
    blah(&foo);
} 

The only reason this compiles is that the blah() fn call will automatically deref (aka autoderef) the provided foo value in order to arrive at the necessary &str type.

That is true but what I meant is there is no conversion of &X to X. Deref conversion is always &X to &Y and this is (I think calling it deref conversion is confusing).

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.