Reputation: 1135
To learn Rust, I'm building my own Matrix class. My implementation of the Add trait is as follows:
impl<T: Add> Add for Matrix<T>
{
type Output = Matrix<T>;
fn add(self, _rhs: Matrix<T>) -> Matrix<T>
{
assert!(self.rows == _rhs.rows && self.cols == _rhs.cols,
"attempting to add matrices of different sizes");
let mut res: Matrix<T> = Matrix::<T>{
rows: self.rows,
cols: self.cols,
data : Vec::<T>::with_capacity(self.rows * self.cols),
};
for i in 0..self.rows*self.cols{
res.data.push(self.data[i] + _rhs.data[i]);
}
res
}
}
but i get the following error
Compiling matrix v0.1.0 (file://~/soft/rust/projects/matrix)
src/lib.rs:35:27: 35:54 error: mismatched types:
expected `T`,
found `<T as core::ops::Add>::Output`
(expected type parameter,
found associated type) [E0308]
src/lib.rs:35 res.data.push(self.data[i] + _rhs.data[i]);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
Going by the error report, I guess that I need to indicate somewhere else that T implements the Add trait but anywhere I try to do this I either get the same error or a parsing error.
My definition of Matrix by the way is
pub struct Matrix<T> {
pub rows: usize,
pub cols: usize,
pub data: Vec<T>,
}
Upvotes: 1
Views: 612
Reputation: 102276
Using T: Add
as a bound says that one can write T + T
, but it doesn't place any restriction on the type that will result from that, in particular, it may not be T
. You're relying on it being T
to be able to return Matrix<T>
.
One approach would be to require that T: Add<Output = T>
, so that T + T
returns a T
:
impl<T: Add<Output = T>> Add for Matrix<T> {
...
}
Another approach would be to instead work with whatever output T
wants to give: i.e. your addition would return Matrix<T::Output>
:
impl<T: Add> Add for Matrix<T>
{
type Output = Matrix<T::Output>;
fn add(self, _rhs: Matrix<T>) -> Matrix<T::Output>
{
assert!(self.rows == _rhs.rows && self.cols == _rhs.cols,
"attempting to add matrices of different sizes");
let mut res = Matrix {
rows: self.rows,
cols: self.cols,
data : Vec::with_capacity(self.rows * self.cols),
};
for i in 0..self.rows*self.cols{
res.data.push(self.data[i] + _rhs.data[i]);
}
res
}
}
However, both of these meet a problem:
<anon>:23:27: 23:39 error: cannot move out of indexed content
<anon>:23 res.data.push(self.data[i] + _rhs.data[i]);
^~~~~~~~~~~~
<anon>:23:42: 23:54 error: cannot move out of indexed content
<anon>:23 res.data.push(self.data[i] + _rhs.data[i]);
^~~~~~~~~~~~
Add
/the +
operator takes ownership of its arguments, and one cannot move ownership out of a vector with direct indexing (in general, the compiler can't tell that one won't try to access a moved-out index again, later, which would be a safety problem). Fortunately, there's a solution: vectors support a moving-out iterator, and one can walk over self
and _rhs
moving out in lock-step:
for (a, b) in self.data.into_iter().zip(_rhs.data.into_iter()) {
res.data.push(a + b)
}
The a
and b
variables are both of type T
, i.e. ownership has moved.
Minor note, one can actually "iteratorise" the code even more, writing:
fn add(self, _rhs: Matrix<T>) -> Matrix<T::Output>
{
assert!(self.rows == _rhs.rows && self.cols == _rhs.cols,
"attempting to add matrices of different sizes");
let data = self.data.into_iter()
.zip(_rhs.data.into_iter())
.map(|(a,b)| a + b)
.collect();
Matrix {
rows: self.rows,
cols: self.cols,
data: data
}
}
Upvotes: 4