Невозможно выйти из заимствованного контента/не может выйти за общую ссылку

Я не понимаю ошибку cannot move out of borrowed content. Я получал это много раз и всегда решал, но никогда не понимал почему.

Например:

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

выдает ошибку:

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

В более новых версиях Rust ошибка

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

Я решил это клонированием line:

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

Я не понимаю ошибку даже после прочтения других сообщений, таких как:

Каково происхождение такого рода ошибки?

Ответ 1

Посмотрим на подпись для into_bytes:

fn into_bytes(self) -> Vec<u8>

Это принимает self, а не ссылку на self (&self). Это означает, что self будет потребляться и не будет доступен после вызова. Вместо него вы получаете Vec<u8>. Префикс into_ является распространенным способом обозначения таких методов.

Я не знаю точно, что возвращает ваш метод iter(), но я предполагаю, что он итератор поверх &String, то есть возвращает ссылки на String, но не дает вам права собственности на них, Это означает, что вы не можете вызвать метод, который потребляет значение.

Как вы нашли, одним из решений является использование clone. Это создает дублирующий объект, который у вас есть, и может вызывать into_bytes on. Как отмечают другие комментаторы, вы также можете использовать as_bytes, который принимает &self, поэтому он будет работать на заемное значение. Какой из них вы должны использовать, зависит от вашей конечной цели за то, что вы делаете с указателем.

В большей картине это все связано с понятием собственности. Определенные операции зависят от владения предметом, а другие операции могут уйти с заимствованием объекта (возможно, изменчиво). Ссылка (&foo) не предоставляет права собственности, это просто заем.

Почему интересно использовать self вместо &self в аргументах функции?

Передача собственности - полезная концепция в целом - когда я закончил с чем-то, у кого-то может быть это. В Русте это способ быть более эффективным. Я могу избежать выделения копии, предоставив вам одну копию, а затем выбросив мою копию. Собственность также является наиболее разрешительным государством; если у меня есть объект, который я могу сделать с ним, как пожелаю.


Здесь код, который я создал для тестирования:

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