@ziplane

Вопрос про владение в Rust, что не так?

Доброго времени суток!
Я пытаюсь реализовать умножение матриц и наткнулся на проблему с владениями.
Я получаю ошибку " borrow of moved value: `buf`".
// вектор, где будут хранится  произведения матриц
    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);
            
        }
    }

Сама ошибка
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

Если я изменю на result.push(buf.clone()) то код работает .
Пойдет ли такое решение или я изначально делаю что то не так ?
  • Вопрос задан
  • 369 просмотров
Решения вопроса 1
keksmen
@keksmen
Just a programmer
Дело в том, что, как и сказал компилятор, вы пытаетесь использовать значение после его перемещения.
Рассмотрим участок кода.
// умножаем  слой на предыдущую  матрицу
buf = v.mat_mul(&buf);
// результат записываем в вектор 
result.push(buf);

На первой строчке вы определяете новое значение переменной. На второй - перемещаете значение в вектор.
После перемещения значение в переменной считается недействительным, т.к. его использование может нарушить безопасность памяти. Это один из основных принципов семантики владения.

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


Однако, клонирование - операция дорогая. Учитывая, что для умножения нужна всего-лишь иммутабельная ссылка на матрицу, мы можем переписать код следующим образом:
// вектор, где будут хранится  произведения матриц
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);
}


Метод Vec::<T>::last возвращает Option<&T>, которая содержит ссылку на последний элемент ветора, если таковой существует. Его сложность O(1), поэтому по поводу ухудшения производительности можно не беспокоиться.
Это именно то, для чего и создавалась переменная-буфер. Теперь нам не нужно производить лишнее клонирование.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы