mousetail
mousetail

Reputation: 8010

How to call a method while iterating in Rust

Appologies if this is very simple. I'm learning rust and getting used to the strange borrowing system. Usually, you can get the desired behavior just by changing the syntax of your method calls, however, in this case, there seems to be now way.

A simplified version of my code is this: EventPump if from SDL.

struct Example {
    pump: EventPump
}

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

    fn other_method(&self) {

    }
}

However, I am getting the following error:

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

There is probably some proper way to do this so my struct can maintain ownership of itself, but I have not been able to find it.

I have tried the following:

I could perhaps copy the entire contents of the iterable into a vector or such, but that would defeat the purpose of a iterator, and what if the iterator is not finite? There has to be a better way right...

If someone with more rust experience could help me out it would be much appreciated.

Upvotes: 5

Views: 1594

Answers (2)

attdona
attdona

Reputation: 18983

If you have a pattern like that:

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

The borrow rule that states exactly one mutable reference is broken: there are one mutable and one immutable reference to self.

To avoid the double reference problem consume the iterator and collect the items into a temporary collection, for example into a Vec:

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

    fn other_method(&self) {}
}

Upvotes: 0

Alan Darmasaputra
Alan Darmasaputra

Reputation: 468

If you want an attribute of a struct to be mutable when there is an immutable reference of a struct in the same block, you'll need RefCell. This is called interior mutability.

If an interior mutability of struct Example is desired, then you will need a 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) {

    }
}

Upvotes: 4

Related Questions