Z
Z
ziplane2019-07-24 20:27:18
Rust
ziplane, 2019-07-24 20:27:18

Question about ownership in Rust, what's wrong?

Good day!
I'm trying to implement matrix multiplication and stumbled upon a problem with ownership.
I am getting "borrow of moved value: `buf`" error.

// вектор, где будут хранится  произведения матриц
    let mut result =vec![];
    // матрица которая будет буфером куда будет записано произведение матриц
    let mut buf:Matrix<i32> = Matrix::new(3, 1);

    for (i:usize,v:Matrix) in layers.iter().enumerate() {
        // если слой(матрица) входной то
        if i ==0 {
            // умножаем первый слой на входящую матрицу
             buf = v.mat_mul(&input);
            // результат записываем в вектор результатов 
            result.push(buf);
           
        }else {
            // умножаем  слой на предыдущую  матрицу
            buf = v.mat_mul(&buf);
            // результат записываем в вектор результатов 
            result.push(buf);
            
        }
    }

The mistake itself
let mut buf:Matrix<i32> = Matrix::new(3, 1);
   |         ------- move occurs because `buf` has type `matrix::Matrix<i32>`, which does not implement the `Copy` trait
...
           result.push(buf);
   |                         --- value moved here, in previous iteration of loop
...
80 |             buf = v.mat_mul(&buf);
   |                             ^^^^ value borrowed here after move
81 | 
82 |             result.push(buf);
   |                         --- value moved here, in previous iteration of loop

If I change to result.push(buf.clone()) then the code works.
Will this work or am I doing something wrong in the first place?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
Dmitry Demin, 2019-08-21
@ziplane

The thing is, just like the compiler said, you are trying to use the value after it has been moved.
Let's take a look at the code section.

// умножаем  слой на предыдущую  матрицу
buf = v.mat_mul(&buf);
// результат записываем в вектор 
result.push(buf);

On the first line, you define the new value of the variable. On the second - move the value into a vector.
After the move, the value in the variable is considered invalid, because its use may violate memory security. This is one of the basic tenets of ownership semantics.
Answering your second question: yes, cloning is what you need to use in this case.
// умножаем  слой на предыдущую  матрицу
buf = v.mat_mul(&buf);
// результат записываем в вектор 
result.push(buf.clone());

However, cloning is an expensive operation. Given that the multiplication only needs an immutable reference to the matrix, we can rewrite the code as follows:
// вектор, где будут хранится  произведения матриц
let mut result = vec![];

for v in layers {
  // пытаемся достать предыдущую матрицу
  let previous_matrix = match result.last() {
    Some(last) => last,
    // а если таковой нет - ей станет входная
    None => &input,
  };
  // умножаем  слой на предыдущую (или входную) матрицу
  let next_matrix = v.mat_mul(previous_matrix);
  result.push(next_matrix);
}

The method Vec::<T>::lastreturns Option<&T>, which contains a reference to the last element of the vector, if one exists. Its complexity is O(1), so you don't have to worry about performance degradation.
This is exactly what the buffer variable was created for. Now we don't need to do extra cloning.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question