Как вызвать метод во время итерации в Rust

Извините, если это очень просто. Я изучаю ржавчину и привыкаю к ​​странной системе заимствований. Обычно вы можете получить желаемое поведение, просто изменив синтаксис вызовов вашего метода, однако в этом случае, похоже, теперь есть способ.

Вот упрощенная версия моего кода: EventPump если из SDL.

struct Example {
    pump: EventPump
}

impl Example {
    fn method(&mut self) {
        for event in pump.poll_iter() {
            self.other_method();
        }
    }

    fn other_method(&self) {

    }
}

Однако я получаю следующую ошибку:

error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
  --> src\game.rs:53:67
   |
30 |             for event in self.pump.poll_iter();
   |                          ---------------------
   |                          | 
   |                          mutable borrow occurs here
   |                          mutable borrow later used here
...
53 |                  self.other_method();
   |                  ^^^^ immutable borrow occurs here

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

Я пробовал следующее:

  • Превратите его в цикл while с явным while let event = iterator.next(), та же ошибка
  • Делая функцию изменяемой, теперь в ошибке говорится, что две изменяемые ссылки также не допускаются. Я предполагаю, что вся часть сообщения об ошибке "неизменяемость" на самом деле не имеет значения.

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

Если бы кто-то с большим опытом ржавчины мог мне помочь, я был бы очень признателен.


person mousetail    schedule 29.02.2020    source источник


Ответы (2)


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

Если требуется внутренняя изменчивость struct Example, тогда вам понадобится RefCell.

use sdl2::{EventPump};

struct Example {
    pump: RefCell<EventPump> // wrap in RefCell
}

impl Example {
    // you have to decide whether you want a mutable or immutable chained methods
    // i.e. method and other_method should be of same mutability because
    // other method is called in method
    fn method(&self) {
        // borrow a mutable reference of pump inside a method with immutable self 
        let mut pump = self.pump.borrow_mut();
        for event in pump.poll_iter() {
            self.other_method();
        }
    }

    fn other_method(&self) {

    }
}
person Alan Darmasaputra    schedule 29.02.2020
comment
Спасибо, работает. Однако теперь, если я хочу получить доступ к другому методу на насосе, я должен сделать self.event_pump.as_ref().unwrap().as_ptr().as_ref().unwrap().keyboard_state(), что также небезопасно, поэтому не идеально. - person mousetail; 29.02.2020
comment
@mousetail, к сожалению, придется иметь дело с RefCell. Вы должны вручную вызвать заимствование () или заимствование_mut (), чтобы использовать его. - person Alan Darmasaputra; 29.02.2020

Если у вас есть такой шаблон:

fn method(&mut self) {
    for item in self.some_iterator_that_borrow_mut() {
        self.other_method();
    }
}

Правило заимствования, которое устанавливает ровно одну изменяемую ссылку, нарушено: есть одна изменяемая и одна неизменяемая ссылка на self.

Чтобы избежать проблемы двойной ссылки, используйте итератор и соберите элементы во временную коллекцию, например в Vec:

impl Example {
    fn method(&mut self) {
        for event in self.pump.poll_iter().collect::<Vec<_>>(); {
            self.other_method();
        }
    }

    fn other_method(&self) {}
}
person attdona    schedule 29.02.2020