Невозможно выйти из заемного контента

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

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

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

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

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

2 ответа

Решение

Давайте посмотрим на подпись для into_bytes:

fn into_bytes(self) -> Vec<u8>

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

Я не знаю точно, что ты iter() метод возвращается, но я предполагаю, что это итератор &String то есть он возвращает ссылки на 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() {}

Досадно, что этот вопрос был отмечен как повторяющийся, потому что это еще один пример тупиковой ловушки для новичков в Rust. Новичку очень сложно сделать обобщение на основе этих конкретных ответов, а это значит, что он просто застревает на следующем экземпляре (ссылка: я).

Чтобы сделать небольшое обобщение и помочь разобраться с общими проблемами «не могу выйти», подумайте, почему вообще возникла эта проблема. Есть две основные причины:

  1. Вы не хотите претендовать на право собственности на что-то.
  2. Вы хотите заявить права собственности на что-то.

В первом случае вы непреднамеренно делаете что-то, требующее владения. Тогда решение состоит в том, чтобы найти альтернативу, которая не требует владения. Именно этот случай описан в этом вопросе и ответе. Вместо, который требует владения, поскольку он потребляет свои ресурсы, замените накоторый будет делать то же самое, не претендуя на право собственности.

Причина, по которой решение неочевидно, заключается в том, что компилятор предположил, что вы действительно хотите использоватьи услужливо заменил его на. Таким образом, он выдает вводящую в заблуждение ошибку, сообщая вам, что разыменование не помогло. Вы ломаете голову над тем, как исправить невозможность, что приводит к появлению пластырей типа .

На самом деле вы никогда не хотели разыменовывать ссылку, а просто нужно было найти альтернативу, которая работала бы со ссылкой на заимствование.

Теперь во втором случае (который более актуален в «дубликатах» вопросов и ответов, на которые я ссылался вверху) ошибка та же, но вы случайно заимствовали вместо того, чтобы заявить о праве собственности. Это часто происходит при реализации метода, поскольку методы обычно используют ссылку, а не используют его. Между прочим, это совершенно необязательно — метод может выбрать потребление, а не заимствование, хотя я не могу себе представить, чтобы это было очень практично, если метод не возвращаетназад.

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

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

Однако часто вы не можете внести это изменение. Вместо этого вам придется лишить права собственности. Но вы не можете просто оставить текущего владельца с пустыми руками — вам нужно оставить ему что-то действительное. Единственный известный мне способ сделать это — использовать один из или из. Переход в версию 1.40, безусловно, самый простой — он просто дает владельцу ценность, а вам — право собственности на текущую стоимость. иальтернативы предоставляют предыдущему владельцу что-то еще, что особенно важно, если тип не имеет.

Вот пример до и после:

      pub struct SimpleLinkedList<T> {
    head: Option<Box<Node<T>>>
}

pub struct Node<T> {
    value: T,
    next: Option<Box<Node<T>>>
}

impl<T> SimpleLinkedList<T> {
    pub fn pop(&mut self) -> Option<T> {
        if self.head.is_none() { return None; }

        let value = self.head.unwrap().value;
        self.head = self.head.unwrap().next;
        return Some(value);
    }
}

Это вызывает ошибку «невозможно выйти из-за изменяемой ссылки», поскольку потребляет.

Есть много способов улучшить это, но в целях иллюстрации это все, что необходимо:

      use std::mem;

pub struct SimpleLinkedList<T> {
    head: Option<Box<Node<T>>>
}

pub struct Node<T> {
    value: T,
    next: Option<Box<Node<T>>>
}

impl<T> SimpleLinkedList<T> {
    pub fn pop(&mut self) -> Option<T> {
        if self.is_empty() { return None; }

        let old_head = mem::take(&mut self.head);
        let old_head_unwrapped = old_head.unwrap();
        self.head = old_head_unwrapped.next;
        return Some(old_head_unwrapped.value);
    }
}

Мы все равно заменяем значение, поэтому, вызвав его, мы можем заявить права собственности. Затем мы можем свободно передать егои впоследствии перезаписать оригинал.

Итак, в заключение, почему вы получаете сообщение «невозможно выйти из заимствованного контента/ссылки»? Либо потому, что вам нужно только брать взаймы, и вы должны гарантировать, что все, что вы делаете, никогда не будет потребляться, либо потому, что вы действительно хотите потреблять и должнывладение.

Другие вопросы по тегам