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

127

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 versions plus récentes 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 comme:

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

Peekmo
la source
1
Avez-vous examiné des questions comme celle-ci ? (Btw, les chaînes offrent la .bytes()méthode.)
huon
Oui, je l'ai regardé, mais je n'ai pas compris :( Et ma chaîne est une std :: string :: String, selon la documentation, il n'y a pas de méthode .bytes ()
Peekmo
4
Ça s'appelle.as_bytes()
bluss le
En fait, merci, cela fonctionne as_bytes()sans clonage. Mais je ne comprends toujours pas pourquoi?
Peekmo
Stringobtient la bytesméthode à partir de str.
huon

Réponses:

108

Regardons la signature pour into_bytes:

fn into_bytes(self) -> Vec<u8>

Cela prend self, pas une référence à soi ( &self). Cela signifie que selfcela sera consommé et ne sera pas disponible après l'appel. À sa place, vous obtenez un fichier Vec<u8>. Le préfixe into_est une manière courante de désigner des méthodes comme celle-ci.

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

Comme vous l'avez trouvé, une solution consiste à utiliser clone. Cela crée un objet en double que vous faites propre, et pouvez appeler into_bytessur. Comme d'autres commentateurs le mentionnent, vous pouvez également utiliser as_bytesce qui prend &self, donc cela fonctionnera sur une valeur empruntée. Lequel vous devez 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'objet, et d'autres opérations peuvent se permettre d'emprunter l'objet (peut-être mutuellement). Une référence ( &foo) n'accorde pas la propriété, c'est juste un emprunt.

Pourquoi est-il intéressant d'utiliser à la selfplace des &selfarguments 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. Dans Rust, c'est un moyen d'être plus efficace. Je peux éviter d'attribuer une copie, de vous en donner une, puis de jeter ma copie. La propriété est aussi 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 avec:

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() {}
Shepmaster
la source