Nie można wyjść z wypożyczonych treści / nie można wyjść poza udostępnione odniesienie

127

Nie rozumiem błędu cannot move out of borrowed content. Otrzymałem go wiele razy i zawsze go rozwiązywałem, ale nigdy nie rozumiałem dlaczego.

Na przykład:

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

powoduje błąd:

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

W nowszych wersjach Rusta błąd to

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

Rozwiązałem to przez sklonowanie line:

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

Nie rozumiem błędu nawet po przeczytaniu innych postów typu:

Jakie jest źródło tego rodzaju błędu?

Peekmo
źródło
1
Czy spojrzałeś na takie pytania ? (Btw, smyczki oferują .bytes()metodę.)
Huon
Tak, zajrzałem do tego, ale nie zrozumiałem :( A mój ciąg to std :: string :: String, zgodnie z dokumentacją, nie ma metody .bytes ()
Peekmo
4
Nazywa się.as_bytes()
bluss
W rzeczywistości, dziękuję, działa as_bytes()bez klonowania. Ale nadal nie rozumiem, dlaczego?
Peekmo
Stringpobiera bytesmetodę z str.
huon

Odpowiedzi:

108

Spójrzmy na podpis dla into_bytes:

fn into_bytes(self) -> Vec<u8>

To wymaga self, a nie odniesienia do self ( &self). Oznacza to, że selfzostanie zużyty i nie będzie dostępny po połączeniu. W jego miejsce otrzymasz plik Vec<u8>. Przedrostek into_jest powszechnym sposobem oznaczania takich metod.

Nie wiem dokładnie, co iter()zwraca twoja metoda, ale przypuszczam, że jest to iterator &String, to znaczy zwraca odwołania do a, Stringale nie daje ci do nich własności. Oznacza to, że nie można wywołać metody, która zużywa wartość .

Jak odkryłeś, jednym z rozwiązań jest użycie clone. To tworzy duplikat obiektu, który zrobić samodzielnie, a może zadzwonić into_bytesna. Jak wspominają inni komentatorzy, możesz również użyć tego, as_bytesktóry bierze &self, więc zadziała na pożyczonej wartości. Który z nich należy użyć, zależy od celu końcowego dotyczącego tego, co zrobisz ze wskaźnikiem.

W szerszej perspektywie wszystko to ma związek z pojęciem własności . Niektóre operacje zależą od posiadania przedmiotu, a inne operacje mogą uciec z pożyczeniem obiektu (być może na zmianę). Reference ( &foo) nie przyznaje prawa własności, to tylko pożyczka.

Dlaczego warto używać selfzamiast &selfargumentów funkcji?

Przeniesienie własności jest ogólnie użyteczną koncepcją - kiedy coś skończę, ktoś inny może to mieć. W Rust to sposób na zwiększenie wydajności. Mogę uniknąć przydzielania kopii, dając ci jedną kopię, a następnie wyrzucając moją kopię. Własność jest również najbardziej liberalnym stanem; jeśli posiadam przedmiot, mogę z nim zrobić, jak chcę.


Oto kod, który stworzyłem do przetestowania:

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
źródło