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

127

Я не понимаю ошибки 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() {

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

В чем причина такого рода ошибок?

Peekmo
источник
1
Вы смотрели на такие вопросы ? ( .bytes()
Между прочим
Да, я заглянул в это, но не понял :( И моя строка - это std :: string :: String, согласно документации, метода .bytes () нет
Peekmo
4
Это называется.as_bytes()
bluss
На самом деле спасибо, работает as_bytes()без клонирования. Но я все еще не понимаю, почему?
Peekmo
Stringполучает bytesметод от str.
huon

Ответы:

108

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

fn into_bytes(self) -> Vec<u8>

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

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

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

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

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

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


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

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
источник