Não é possível sair do conteúdo emprestado / não é possível deixar para trás uma referência compartilhada

127

Eu não entendo o erro cannot move out of borrowed content. Eu o recebi muitas vezes e sempre o resolvi, mas nunca entendi o porquê.

Por exemplo:

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

produz o erro:

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

Nas versões mais recentes do Rust, o erro é

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

Eu o resolvi clonando line:

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

Não entendo o erro mesmo depois de ler outras postagens como:

Qual é a origem desse tipo de erro?

Peekmo
fonte
1
Você já olhou para perguntas como esta ? (Btw, cordas oferecem o .bytes()método.)
huon
Sim, eu olhei para ele, mas não entendia :( E a minha string é uma std :: string :: String, de acordo com a documentação, não há .bytes () Método
Peekmo
4
É chamado.as_bytes()
bluss
De fato, obrigado, ele funciona as_bytes()sem clonagem. Mas ainda não entendo o porquê?
Peekmo
Stringobtém o bytesmétodo de str.
huon

Respostas:

108

Vejamos a assinatura para into_bytes:

fn into_bytes(self) -> Vec<u8>

Isso leva self, não uma referência a self ( &self). Isso significa que selfserá consumido e não estará disponível após a chamada. Em seu lugar, você recebe um Vec<u8>. O prefixo into_é uma maneira comum de denotar métodos como este.

Não sei exatamente o que seu iter()método retorna, mas meu palpite é que é um iterador encerrado &String, ou seja, retorna referências a um, Stringmas não lhe dá a propriedade. Isso significa que você não pode chamar um método que consome o valor .

Como você encontrou, uma solução é usar clone. Isso cria um objeto duplicado que você faz próprios, e pode chamar into_bytesdiante. Como outros comentadores mencionam, você também pode usar as as_bytestomadas &self, para que funcionem com um valor emprestado. Qual deles você deve usar depende do seu objetivo final para o que você faz com o ponteiro.

No quadro geral, tudo isso tem a ver com a noção de propriedade . Certas operações dependem da propriedade do item, e outras operações podem evitar o empréstimo do objeto (talvez mutuamente). Uma referência ( &foo) não concede propriedade, é apenas um empréstimo.

Por que é interessante usar em selfvez de &selfnos argumentos de uma função?

Transferir propriedade é um conceito útil em geral - quando eu termino alguma coisa, alguém pode tê-la. No Rust, é uma maneira de ser mais eficiente. Eu posso evitar alocar uma cópia, fornecendo uma cópia e depois jogando fora a minha cópia. A propriedade também é o estado mais permissivo; se eu possuo um objeto, posso fazê-lo como desejar.


Aqui está o código que eu criei para testar:

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
fonte