web-dev-qa-db-fra.com

Que dois-je faire pour résoudre une erreur "utilisation de la valeur déplacée"?

J'essaie de calculer le 10 001e premier dans Rust (Project Euler 7), et dans le cadre de cela, ma méthode pour vérifier si un entier est premier fait référence à un vecteur:

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
}

Lorsque j'essaie de référencer le vecteur, j'obtiens l'erreur suivante:

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

Que dois-je faire pour primes afin de pouvoir y accéder dans la fonction vectorIsPrime?

31
mjkaufer

Avec la définition actuelle de votre fonction vectorIsPrime(), la fonction spécifie qu'elle requiert la propriété du paramètre car vous le passez par valeur .

Lorsqu'une fonction requiert un paramètre par valeur, le compilateur vérifie si la valeur peut être copiée en vérifiant si elle implémente le trait Copy.

  • Si c'est le cas, la valeur est copiée (avec un memcpy) et donnée à la fonction, et vous pouvez toujours continuer à utiliser votre valeur d'origine.
  • Si ce n'est pas le cas, la valeur est déplacée vers la fonction donnée et l'appelant ne peut pas l'utiliser par la suite

C'est la signification du message d'erreur que vous avez.

Cependant, la plupart des fonctions ne nécessitent pas la propriété des paramètres: elles peuvent travailler sur des "références empruntées", ce qui signifie qu'elles ne possèdent pas réellement la valeur (et ne peuvent pas par exemple la mettre dans un conteneur ou la détruire).

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
}

La fonction vector_is_prime() spécifie maintenant qu'elle n'a besoin que d'une tranche , c'est-à-dire un pointeur emprunté vers un tableau (y compris sa taille) que vous peut obtenir à partir d'un vecteur en utilisant l'opérateur d'emprunt &.

Pour plus d'informations sur la propriété, je vous invite à lire la partie du livre traitant de propriété .

37
Vaelden

La rouille est, comme je dirais, un langage "axé sur les valeurs". Cela signifie que si vous définissez des nombres premiers comme celui-ci

let primes: Vec<u64> = …

ce n'est pas une référence à un vecteur. C'est pratiquement une variable qui stocke une valeur de type Vec<u64> Comme toute variable u64 Stocke une valeur u64. Cela signifie que si vous le passez à une fonction définie comme ceci

fn vec_is_prime(num: u64, vec: Vec<u64>) -> bool { … }

la fonction obtiendra sa propre valeur u64 et sa propre valeur Vec<u64>.

La différence entre u64 Et Vec<u64> Est cependant qu'une valeur u64 Peut être facilement copiée vers un autre endroit alors qu'une valeur Vec<u64> Ne peut que déplacer vers un autre endroit facilement. Si vous voulez donner à la fonction vec_is_prime Sa propre valeur Vec<u64> Tout en en gardant une pour vous-même, vous devez la dupliquer d'une manière ou d'une autre. C'est à cela que sert clone(). La raison pour laquelle vous devez être explicite ici est que cette opération n'est pas bon marché. C'est une bonne chose à propos de Rust: il n'est pas difficile de repérer des opérations coûteuses. Donc, vous pourriez appeler la fonction comme ceci

if vec_is_prime(num, primes.clone()) { …

mais ce n'est pas vraiment ce que vous voulez, en fait. La fonction n'a pas besoin de sa propre valeur Vec<64>. Il a juste besoin de l'emprunter pour une courte période. L'emprunt est beaucoup plus efficace et applicable dans ce cas:

fn vec_is_prime(num: u64, vec: &Vec<u64>) -> bool { …

L'invoquer nécessite désormais "l'opérateur emprunteur":

if vec_is_prime(num, &primes) { …

Bien mieux. Mais nous pouvons encore l'améliorer. Si une fonction veut emprunter un Vec<T> Juste pour le lire, il vaut mieux prendre un &[T] À la place:

fn vec_is_prime(num: u64, vec: &[u64]) -> bool { …

C'est juste plus général. Maintenant, vous pouvez prêter une certaine partie d'un Vec à la fonction ou à quelque chose d'autre (pas nécessairement un Vec, tant que ce quelque chose stocke ses valeurs consécutivement en mémoire, comme une table de recherche statique). Ce qui est aussi bien, c'est qu'en raison des règles de conversion, vous n'avez rien à modifier sur le site de l'appel. Vous pouvez toujours appeler cette fonction avec &primes Comme argument.

Pour String et &str La situation est la même. String sert à stocker des valeurs de chaîne dans le sens où une variable de ce type possède cette valeur. &str Sert à les emprunter.

34
sellibitze

Vous déplacez la valeur de primes vers la fonction vectorIsPrime (BTW Rust use snake_case par convention). Vous avez d'autres options, mais la meilleure consiste à emprunter un vecteur au lieu de le déplacer:

fn vector_is_prime(num: u64, p: &Vec<u64>) -> bool { … }

Et puis en passant la référence:

vector_is_prime(num, &primes)
3
Hauleth