Что мне нужно сделать, чтобы устранить ошибку "использования перемещенного значения"?
Я пытаюсь вычислить 10,001-е простое число в Rust (Project Euler 7), и как часть этого мой метод проверки, является ли целое число простым числом, ссылается на вектор:
fn main() {
let mut count: u32 = 1;
let mut num: u64 = 1;
let mut primes: Vec<u64> = Vec::new();
primes.push(2);
while count < 10001 {
num += 2;
if vectorIsPrime(num, primes) {
count += 1;
primes.push(num);
}
}
}
fn vectorIsPrime(num: u64, p: Vec<u64>) -> bool {
for i in p {
if num > i && num % i != 0 {
return false;
}
}
true
}
Когда я пытаюсь сослаться на вектор, я получаю следующую ошибку:
error[E0382]: use of moved value: 'primes'
--> src/main.rs:9:31
|
9 | if vectorIsPrime(num, primes) {
| ^^^^^^ value moved here, in previous iteration of loop
|
= note: move occurs because 'primes' has type 'std::vec::Vec<u64>', which does not implement the 'Copy' trait
Что я должен сделать с primes
, чтобы иметь возможность получить к нему доступ в функции vectorIsPrime
?
Ответы
Ответ 1
С текущим определением вашей функции vectorIsPrime()
функция указывает, что она требует владения параметром, потому что вы передаете его по значению.
Когда функции требуется параметр по значению, компилятор проверит, можно ли скопировать значение, проверив, реализует ли он свойство Copy
.
- Если это так, значение копируется (с помощью memcpy) и передается функции, и вы все равно можете продолжать использовать исходное значение.
- Если это не так, то значение перемещается в данную функцию, и вызывающая сторона не может использовать его впоследствии
В этом смысл вашего сообщения об ошибке.
Однако большинство функций не требуют владения параметрами: они могут работать с "заимствованными ссылками", что означает, что они на самом деле не владеют значением (и, например, не могут поместить его в контейнер или уничтожить).
fn main() {
let mut count: u32 = 1;
let mut num: u64 = 1;
let mut primes: Vec<u64> = Vec::new();
primes.push(2);
while count < 10001 {
num += 2;
if vector_is_prime(num, &primes) {
count += 1;
primes.push(num);
}
}
}
fn vector_is_prime(num: u64, p: &[u64]) -> bool {
for &i in p {
if num > i && num % i != 0 {
return false;
}
}
true
}
Функция vector_is_prime()
теперь указывает, что ей нужен только срез, то есть заимствованный указатель на массив (включая его размер), который можно получить из вектора с помощью оператора заимствования &
.
Для получения дополнительной информации о собственности, я предлагаю вам прочитать часть книги, посвященную собственности.
Ответ 2
Rust - это, как я бы сказал, "ценностно-ориентированный" язык. Это означает, что если вы определяете простые числа, такие как
let primes: Vec<u64> = …
это не ссылка на вектор. Это практически переменная, которая хранит значение типа Vec<u64>
, как и любая переменная u64
, сохраняет значение u64
. Это означает, что если вы передадите его функции, определенной как
fn vec_is_prime(num: u64, vec: Vec<u64>) -> bool { … }
функция получит свое собственное значение u64
и собственное значение Vec<u64>
.
Разница между u64
и Vec<u64>
заключается в том, что значение u64
можно легко скопировать в другое место, в то время как значение Vec<u64>
может легко перемещаться в другое место. Если вы хотите присвоить функции vec_is_prime
собственное значение Vec<u64>
, сохранив ее для себя в основном, вам нужно как-то ее дублировать. Это для clone()
. Причина, по которой вы должны быть здесь ясны, заключается в том, что эта операция не из дешевых. Это одна хорошая вещь о Rust: Это не сложно определить дорогие операции. Таким образом, вы можете вызвать функцию, подобную этой
if vec_is_prime(num, primes.clone()) { …
но на самом деле это не совсем то, что вы хотите. Функция не нуждается в ее собственном значении Vec<64>
. Просто нужно заимствовать его на короткое время. Заимствование гораздо более эффективно и применимо в этом случае:
fn vec_is_prime(num: u64, vec: &Vec<u64>) -> bool { …
При вызове его теперь требуется "оператор заимствования":
if vec_is_prime(num, &primes) { …
Гораздо лучше. Но мы все равно можем его улучшить. Если функция хочет заимствовать Vec<T>
только для ее чтения, лучше взять вместо этого &[T]
:
fn vec_is_prime(num: u64, vec: &[u64]) -> bool { …
Это просто более общий. Теперь вы можете предоставить определенную часть Vec функции или что-то еще полностью (не обязательно a Vec
, если это что-то хранит свои значения последовательно в памяти, как статическая таблица поиска). Также приятно, что из-за правил конвертации вам не нужно ничего менять на сайте вызова. Вы все еще можете вызвать эту функцию с помощью &primes
в качестве аргумента.
Для String
и &str
ситуация одинаков. String
предназначен для хранения строковых значений в том смысле, что переменной этого типа принадлежит это значение. &str
предназначен для их заимствования.
Ответ 3
Вы перемещаете значение primes
в функцию vectorIsPrime
(BTW Rust использует snake_case
по соглашению). У вас есть другие варианты, но лучше всего занять вектор, а не перемещать его:
fn vector_is_prime(num: u64, p: &Vec<u64>) -> bool { … }
И затем передавая ссылку на него:
vector_is_prime(num, &primes)