Как бороться с несколькими изменчивыми заимствованиями в Rust с match? - PullRequest
0 голосов
/ 15 мая 2018

Я пытаюсь инкапсулировать синтаксический анализатор XML, используя quick-xml, который принимает какой-то конкретный XML в записи.Основной код следующий:

pub struct GPXParser<B: BufRead> {
    reader: Reader<B>,
    buff: Vec<u8>,
}

impl<B> GPXParser<B> {
    pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
        match self.reader.read_event(&mut self.buff) {
            Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => {
                self.read_next_wpt() // --> Multiple mutable borrows error
            }
            _ => Err(Error::NoCaches),
        }
    }

    fn read_next_wpt(&mut self) -> Result<XMLCache, Error> {
        match self.reader.read_event(&mut self.buff) {
            _ => Err(Error::NoCaches),
        }
    }
}

Затем я прочитал эту тему на rust-lang.org, отметив, что:

идиоматический код Rust обычноизбегает хранения долговременных ссылок на внутренние объекты изменяемых объектов и предпочитает альтернативные подходы, использующие неизменяемые ссылки или независимые значения

Я пытался изменить свой подход, используя промежуточный элемент buff:

pub struct GPXParser<B: BufRead> {
    reader: Reader<B>,
}

impl<B> GPXParser<B> {
    pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
        let mut buff: Vec<u8> = Vec::new();

        match self.reader.read_event(&mut buff) {
            Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => {
                self.read_next_wpt(&mut buff) // --> Multiple mutable borrows error
            }
            _ => Err(Error::NoCaches),
        }
    }

    fn read_next_wpt(&mut self, buff: &mut Vec<u8>) -> Result<XMLCache, Error> {
        match self.reader.read_event(buff) {
            _ => Err(Error::NoCaches),
        }
    }
}

Я не изменил подход.

Я попытался разбить оператор match на две строки:

pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
    let result = self.reader.read_event(&mut self.buff);
    match result {
        Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => self.read_next_wpt(),
        _ => Err(Error::NoCaches),
    }
}

Но я получаю ту же ошибку:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/gpx/mod.rs:34:17
   |
31 |         let result = self.reader.read_event(&mut self.buff);
   |                                                  --------- first mutable borrow occurs here
...
34 |                 self.read_next_wpt()
   |                 ^^^^ second mutable borrow occurs here
...
39 |     }
   |     - first borrow ends here

Есть ли способ использовать один и тот же буфер для всех вызовов разных методов?

Если в игру придет какое-то время жизни?

Если нет, каким будет идиоматический подход Rust?решить эту проблему?

1 Ответ

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

Проблема связана с временем жизни 'b в прототипе для read_event :

pub fn read_event<'a, 'b>(
    &'a mut self, 
    buf: &'b mut Vec<u8>
) -> Result<Event<'b>>

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

pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
    match self.reader.read_event(&mut self.buff) {
        Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => (),
        _ => { return Err(Error::NoCaches) },
    }
    self.read_next_wpt()
}

Если вы хотите добавить больше дел, вы должны сначала извлечь любыесоответствующую информацию, которую вы хотите использовать из результата, включая любую информацию, необходимую для выбора метода для вызова, затем выведите результат из области видимости, прежде чем вызывать метод.Например:

pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
    let next_method = match self.reader.read_event(&mut self.buff) {
        Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => GPXParser<B>::read_next_wpt,
        Ok(XMLEvent::Start(ref e)) if e.name() == b"foo" => GPXParser<B>::read_next_foo,
        _ => { return Err(Error::NoCaches) },
    }
    next_method(self)
}

Или может быть проще каждый раз использовать другой буфер, если падение производительности достаточно мало, чтобы быть приемлемым (вы должны измерить его, чтобы подтвердить свое решение).


Исходный ответ ниже для справки:

Попробуйте разбить read_event и совпадение на отдельные строки:

let result = self.reader.read_event(&mut self.buff);
match result {
    ...
}

Ваша проблема в том,что буфер заимствован изменчиво для всего выражения совпадения, поэтому вы не можете повторно заимствовать буфер внутри плеча совпадения.Разделив код на две строки, буфер заимствуется только для первого выражения (let result=...) и может быть снова заимствован в совпадении.

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

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