Impossibile spostarsi dal contenuto preso in prestito / impossibile spostarsi da dietro un riferimento condiviso


127

Non capisco l'errore cannot move out of borrowed content. L'ho ricevuto molte volte e l'ho sempre risolto, ma non ho mai capito il perché.

Per esempio:

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);
}

produce l'errore:

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

Nelle versioni più recenti di Rust, l'errore è

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

L'ho risolto clonando line:

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

Non capisco l'errore anche dopo aver letto altri post come:

Qual è l'origine di questo tipo di errore?


1
Hai esaminato domande come questa ? (A proposito, le stringhe offrono il .bytes()metodo.)
huon,

Sì, l'ho esaminato, ma non ho capito :( E la mia stringa è uno std :: string :: String, secondo la documentazione, non esiste un metodo .bytes ()
Peekmo,

4
Si chiama.as_bytes()
bluss

In effetti, grazie, funziona as_bytes()senza clonazione. Ma ancora non capisco perché?
Peekmo,

Stringottiene il bytesmetodo da str.
huon,

Risposte:


108

Diamo un'occhiata alla firma per into_bytes:

fn into_bytes(self) -> Vec<u8>

Questo richiede self, non un riferimento a self ( &self). Ciò significa che selfverrà consumato e non sarà disponibile dopo la chiamata. Al suo posto, ottieni un Vec<u8>. Il prefisso into_è un modo comune di indicare metodi come questo.

Non so esattamente cosa iter()ritorni il tuo metodo, ma la mia ipotesi è che sia un iteratore &String, ovvero, restituisca riferimenti a un Stringma non ti dia la proprietà di essi. Ciò significa che non è possibile chiamare un metodo che consuma il valore .

Come hai trovato, una soluzione è quella di utilizzare clone. Questo crea un oggetto duplicato che possiedi e su cui puoi fare appello into_bytes. Come altri commentatori menzionano, puoi anche usare ciò as_bytesche richiede &self, quindi funzionerà su un valore preso in prestito. Quello che dovresti usare dipende dal tuo obiettivo finale per quello che fai con il puntatore.

Nel quadro più ampio, tutto ciò ha a che fare con la nozione di proprietà . Alcune operazioni dipendono dal possesso dell'oggetto e altre operazioni possono cavarsela prendendo in prestito l'oggetto (forse mutuamente). Un riferimento ( &foo) non garantisce la proprietà, è solo un prestito.

Perché è interessante usare selfinvece che &selfnegli argomenti di una funzione?

Il trasferimento della proprietà è un concetto utile in generale - quando ho finito con qualcosa, qualcun altro potrebbe averlo. In Rust, è un modo per essere più efficienti. Posso evitare di allocare una copia, dandone una copia, quindi buttare via la mia copia. La proprietà è anche lo stato più permissivo; se possiedo un oggetto posso farcela come desidero.


Ecco il codice che ho creato per testare con:

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() {}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.