Ошибка двойного изменяемого заимствования в цикле происходит даже при включенном NLL - PullRequest
0 голосов
/ 25 мая 2018

Предположим, у меня есть несколько структур, как в следующем примере, и в методе next() мне нужно извлечь следующее событие, используя предоставленный пользователем буфер, но если это событие является комментарием, а флаг игнорируемых комментариев установлен направда, мне нужно снова получить следующее событие:

struct Parser {
    ignore_comments: bool,
}

enum XmlEvent<'buf> {
    Comment(&'buf str),
    Other(&'buf str),
}

impl Parser {
    fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
        let result = loop {
            buffer.clear();

            let temp_event = self.parse_outside_tag(buffer);

            match temp_event {
                XmlEvent::Comment(_) if self.ignore_comments => {}
                _ => break temp_event,
            }
        };
        result
    }

    fn parse_outside_tag<'buf>(&mut self, _buffer: &'buf mut String) -> XmlEvent<'buf> {
        unimplemented!()
    }
}

Этот код, однако, выдает двойную ошибку заимствования, даже когда у меня включено #![feature(nll)]:

error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
  --> src/main.rs:14:13
   |
14 |             buffer.clear();
   |             ^^^^^^ second mutable borrow occurs here
15 |             
16 |             let temp_event = self.parse_outside_tag(buffer);
   |                                                     ------ first mutable borrow occurs here
   |
note: borrowed value must be valid for the lifetime 'buf as defined on the method body at 12:5...
  --> src/main.rs:12:5
   |
12 |     fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
  --> src/main.rs:16:53
   |
16 |             let temp_event = self.parse_outside_tag(buffer);
   |                                                     ^^^^^^ mutable borrow starts here in previous iteration of loop
   |
note: borrowed value must be valid for the lifetime 'buf as defined on the method body at 12:5...
  --> src/main.rs:12:5
   |
12 |     fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Iможет (хотя бы приблизительно) понять, почему здесь может происходить ошибка с отключенной функцией NLL, но я не понимаю, почему это происходит с NLL.

В любом случае, моя конечная цель - реализовать это без флагов,поэтому я также попытался сделать это (это рекурсивно, что очень печально, но все нерекурсивные версии, которые я придумал, не могли работать без NLL):

fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
    buffer.clear();

    {
        let temp_event = self.parse_outside_tag(buffer);

        match temp_event {
            XmlEvent::Comment(_) if self.ignore_comments => {}
            _ => return temp_event,
        }
    }

    self.next(buffer)
}

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

error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
  --> src/main.rs:23:19
   |
15 |             let temp_event = self.parse_outside_tag(buffer);
   |                                                     ------ first mutable borrow occurs here
...
23 |         self.next(buffer)
   |                   ^^^^^^ second mutable borrow occurs here
24 |     }
   |     - first borrow ends here

error: aborting due to previous error

И снова, NLL не исправляет это.

Прошло много времени с тех пор, как я столкнулся с ошибкой проверки заимствования, которую я получаюЯ не понимаю, поэтому я надеюсь, что на самом деле это нечто простое, что я почему-то упускаю из виду :)

Я действительно подозреваю, что основная причина так или иначе связана с явным временем жизни 'buf (в частности,ошибки, отмеченные флагом NLL, содержат эти примечания), но я не могу понять, что именно здесь не так.

1 Ответ

0 голосов
/ 28 мая 2018

Это ограничение текущей реализации из нелексических времен жизни Это может быть показано с помощью этого уменьшенного случая:

fn next<'buf>(buffer: &'buf mut String) -> &'buf str {
    loop {
        let event = parse(buffer);

        if true {
            return event;
        }
    }
}

fn parse<'buf>(_buffer: &'buf mut String) -> &'buf str {
    unimplemented!()
}

fn main() {}

Это ограничение предотвращает NLL case # 3 : поток условного управления через функции

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

Я спросил Нико Мацакиса об этом коде :

В контексте вашего примера: значение event должно иметь время жизни 'buf только условно - в точке возврата, которая может выполняться или не выполняться.Но когда мы «нечувствительны к местоположению», мы просто отслеживаем время жизни, которое event должно иметь где угодно, без учета того, где должно храниться это время жизни.В этом случае это означает, что мы сохраняем его везде, поэтому вы получаете ошибку компиляции.

Одна тонкая вещь заключается в том, что текущий анализ чувствителен к местоположению в одном отношении - где происходит заимствование.Длина заимствования не равна.

Хорошая новость заключается в том, что добавление этой концепции чувствительности к местоположению рассматривается как улучшение реализации нелексических времен жизни.Плохая новость:

Это может быть или не быть до выпуска [Rust 2018].

(Примечание: не сделали этов первоначальный выпуск Rust 2018)

Это зависит от (еще более новой!) базовой реализации нелексических сроков службы, которая повышает производительность.Вы можете подписаться на эту наполовину реализованную версию, используя -Z polonius:

rustc +nightly -Zpolonius --edition=2018 example.rs
RUSTFLAGS="-Zpolonius" cargo +nightly build

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...