0

I'm trying to learn rust making a game with SDL2. I have a struct GameEngine which has ownership of some variables to control the game state.

The method GameEngine::run() is responsible to manage the game loop. I want this method to do 2 things:

  1. Check if some event is related to closing the game and in this case break the loop
  2. For any other kind of event I want to call a method GameEngine::handle_event() to handle it

The problem is that the compiler is refusing to accept my code telling me I'm trying to borrow self as mutable more than once. The first borrow happen on this line:

let event_poll_iterator = self.event_pump.poll_iter();

and the second on this:

self.handle_event(event);

As I'm a newbie in Rust, I'm getting stuck in this error.

The complete code:

pub mod engine {
    use std::time::Duration;

    use sdl2::{EventPump, Sdl};
    use sdl2::event::Event;
    use sdl2::keyboard::Keycode;
    use sdl2::pixels::Color;
    use sdl2::render::WindowCanvas;

    fn get_canvas(sdl_context: &Sdl) -> WindowCanvas {
        let video_subsystem = sdl_context.video().unwrap();

        let window = video_subsystem
            .window("SDL2 Snake Game", 800, 600)
            .position_centered()
            .opengl()
            .build()
            .map_err(|e| e.to_string()).unwrap();

        let mut canvas = window.into_canvas().build().map_err(|e| e.to_string()).unwrap();

        canvas.set_draw_color(Color::BLACK);

        canvas
    }

    pub struct GameEngine {
        context: Sdl,
        event_pump: EventPump,
        canvas: WindowCanvas,
    }

    impl GameEngine {
        pub fn new() -> Self {
            let context = sdl2::init().unwrap();
            let canvas = get_canvas(&context);
            let event_pump = context.event_pump().unwrap();
            GameEngine { context, canvas, event_pump }
        }

        fn redraw(&mut self) {
            self.canvas.clear();
            self.canvas.present();
        }

        fn handle_event(&mut self, event: Event) {
            todo!()
        }

        pub fn run(&mut self) {
            'game_loop: loop {
                let event_poll_iterator = self.event_pump.poll_iter();

                for event in event_poll_iterator {
                    match event {
                        Event::Quit { .. }
                        | Event::KeyDown {
                            keycode: Some(Keycode::Escape),
                            ..
                        } => break 'game_loop,
                        _ => {
                            self.handle_event(event);
                        }
                    }
                }

                self.redraw();
                std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 30));
            }
        }
    }
}

Edit

I could reproduce the same problem (I think) with a much smaller example:

struct List {
    v: Vec<u32>
}

impl List {
    fn increment(&mut self, x: &mut u32) {
        *x += 1;
    }

    fn iter(&mut self) {
        for x in &mut self.v {
            self.increment(x);
        }
    }
}

fn main() {
    let mut list = List { v: vec![1, 2, 3] };
    list.iter();
    assert!(list.v == vec![2, 3, 4]);
}

Error log:

λ cargo run
   Compiling rustlings v4.7.1 (/home/luizalabs/repositories/rust/rustlings)
error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:12:13
   |
11 |         for x in &mut self.v {
   |                  -----------
   |                  |
   |                  first mutable borrow occurs here
   |                  first borrow later used here
12 |             self.increment(x);
   |             ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `rustlings` due to previous error
Michael Pacheco
  • 948
  • 1
  • 17
  • 25
  • Please always post the full error too. – Chayim Friedman Jun 03 '22 at 02:53
  • 2
    Do you understand that you cannot borrow a variable mutably twice? Have you read the book? – Chayim Friedman Jun 03 '22 at 02:56
  • Sorry, Chayim. I'll add it. Yes, I read this part. We can have as many immutable references as we would like but at most one mutable ref and cannot have both mutable and immutable refs in the same scope – Michael Pacheco Jun 03 '22 at 11:18
  • @ChayimFriedman I have edited the question with a simpler example. Could you take a look please? – Michael Pacheco Jun 03 '22 at 14:39
  • Does this answer your question? [How to solve error "cannot borrow \`\*self\` as mutable more than once at a time"](https://stackoverflow.com/questions/69128621/how-to-solve-error-cannot-borrow-self-as-mutable-more-than-once-at-a-time) – Chayim Friedman Jun 05 '22 at 20:39
  • The example was not the problem; the problem is that I'm not sure what you don't understand. I _guessed_, and that is why I voted for the duplicate now, that you are confused because it works without the method call - but you never said that explicitly. – Chayim Friedman Jun 05 '22 at 20:41

2 Answers2

1

The problem is, self.handle_event could modify whatever you are iterating over (in this case event_poll_iterator). If you are modifying what the iterator is iterating over, you might want to consider cloning the iterator. If self.handle_event isn't modifying the iterator, you have to show the borrow checker that you are not modifying the iterator. One option is to inline self.handle_event. Another option is to pass whatever you are modifying as mutable to self.handle_event. Here is a simple example of what is going on:

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32
}

struct Points {
    points: Vec<Point>,
    num_points: usize
}

impl Points {
    fn bar(&mut self, point: &mut Point) {
        println!("{:?}", point)
    }

    fn foo(&mut self) {
        for p in &mut self.points {
            self.bar(p);
        }
    }
}

Inlining would change foo as such:

fn foo(&mut self) {
    for p in &mut self.points {
        println!("{:?}", p); // modified line
    }
}
obr
  • 176
  • 2
  • 6
  • Why your example didn't work when I replaced Vec to Vec? – Michael Pacheco Jun 03 '22 at 11:39
  • Please, see my question update – Michael Pacheco Jun 03 '22 at 14:39
  • 1
    @MichaelPacheco -- About the question update. First, remove the `&mut self` in the definition of `increment`. It still won't compile, because now you're borrowing `self` as mutable, and then borrowing `self` as immutable. To fix this, you completely remove `&self` from the definition of `increment`. Just take what you need to modify (in this case, `x: &mut u32`). Here is your modified code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fd359999b476faf639391fda5668b5f7 – obr Jun 03 '22 at 16:34
  • But what if I need to acccess some other internal state in `increment()`? Thats the original problem I'm facing. In the `handle_event` I'll mutate the `self` state based on which value the event have – Michael Pacheco Jun 03 '22 at 18:29
  • @MichaelPacheco -- Then you pass `event` as immutable, and whatever you have to mutate as mutable (or you should maybe just inline that code). – obr Jun 03 '22 at 18:42
0

Tried to simplify your example

fn main() {
    let i: i32 = 4326;

    let b = i.to_le_bytes();

    for i in 0..4 {
        println!("{}", b[i]);
    }
}

pub struct GameEngine {
    event_pump: EventPump,
}

pub struct EventPump {
}

impl EventPump {
    pub fn poll_iter(&mut self) -> Vec<i32>  {
        vec![0, 1, 2]
    }
}

impl GameEngine {
    pub fn new() -> Self {
        GameEngine {
            event_pump: EventPump { },
        }
    }

    fn redraw(&mut self) {
    }

    fn handle_event(&mut self, _event: i32) {
        todo!()
    }

    pub fn run(&mut self) {
        loop {
        let ep = self.event_pump.poll_iter();
            let event_poll_iterator = ep.iter();

            for event in event_poll_iterator {
                match event {
                    _ => {
                        self.handle_event(*event);
                    }
                }
            }

            self.redraw();
        }
    }
}

Hope without losing sense. Seems it compiled ok. It changes Iter to vector instance but I'm not sure if that matters.

STO
  • 10,390
  • 8
  • 32
  • 32