web-dev-qa-db-fra.com

Impossible de sortir du contenu emprunté / de derrière une référence partagée

Je ne comprends pas l'erreur cannot move out of borrowed content. Je l'ai reçu plusieurs fois et je l'ai toujours résolu, mais je n'ai jamais compris pourquoi.

Par exemple:

for line in self.xslg_file.iter() {
    self.buffer.clear();

    for current_char in line.into_bytes().iter() {
        self.buffer.Push(*current_char as char);
    }

    println!("{}", line);
}

produit l'erreur:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:31:33
   |
31 |             for current_char in line.into_bytes().iter() {
   |                                 ^^^^ cannot move out of borrowed content

Dans les nouvelles versions de Rust, l'erreur est

error[E0507]: cannot move out of `*line` which is behind a shared reference
  --> src/main.rs:31:33
   |
31 |             for current_char in line.into_bytes().iter() {
   |                                 ^^^^ move occurs because `*line` has type `std::string::String`, which does not implement the `Copy` trait

Je l'ai résolu en clonant line:

for current_char in line.clone().into_bytes().iter() {

Je ne comprends pas l'erreur même après avoir lu d'autres articles tels que:

Quelle est l'origine de ce genre d'erreur?

108
Peekmo

Regardons la signature pour into_bytes :

_fn into_bytes(self) -> Vec<u8>
_

Cela prend self, pas une référence à self (_&self_). Cela signifie que self sera consommé et ne sera plus disponible après l'appel. À sa place, vous obtenez un _Vec<u8>_. Le préfixe _into__ est un moyen courant de désigner des méthodes comme celle-ci.

Je ne sais pas exactement ce que retourne votre méthode iter(), mais je suppose que c'est un itérateur sur _&String_, c'est-à-dire qu'il renvoie des références à String mais ne vous donne pas la propriété d'eux. Cela signifie que vous ne pouvez pas appeler une méthode qui consomme la valeur .

Comme vous l'avez constaté, une solution consiste à utiliser clone. Cela crée un objet en double que vous possédez et que vous pouvez appeler _into_bytes_ on. Comme d'autres intervenants le mentionnent, vous pouvez également utiliser as_bytes qui prend _&self_, de sorte que cela fonctionnera avec une valeur empruntée. Lequel vous devriez utiliser dépend de votre objectif final pour ce que vous faites avec le pointeur.

Dans l’ensemble, tout cela a à voir avec la notion de propriété . Certaines opérations dépendent de la possession de l'élément, et d'autres opérations peuvent se permettre d'emprunter l'objet (peut-être de manière mutuelle). Une référence (_&foo_) n'accorde pas la propriété, il s'agit simplement d'un emprunt.

Pourquoi est-il intéressant d'utiliser self au lieu de _&self_ dans les arguments d'une fonction?

Le transfert de propriété est un concept utile en général - quand j'en ai fini avec quelque chose, quelqu'un d'autre peut l'avoir. À Rust, c'est un moyen d'être plus efficace. Je peux éviter d'allouer une copie, de vous en donner une, puis de jeter ma copie. La propriété est également l’état le plus permissif; si je possède un objet, je peux en faire ce que je veux.


Voici le code que j'ai créé pour tester:

_struct IteratorOfStringReference<'a>(&'a String);

impl<'a> Iterator for IteratorOfStringReference<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

struct FileLikeThing {
    string: String,
}

impl FileLikeThing {
    fn iter(&self) -> IteratorOfStringReference {
        IteratorOfStringReference(&self.string)
    }
}

struct Dummy {
    xslg_file: FileLikeThing,
    buffer: String,
}

impl Dummy {
    fn dummy(&mut self) {
        for line in self.xslg_file.iter() {
            self.buffer.clear();

            for current_char in line.into_bytes().iter() {
                self.buffer.Push(*current_char as char);
            }

            println!("{}", line);
        }
    }
}

fn main() {}
_
94
Shepmaster