Borrowing and the self keyword in implemented methods

Hey there!

I'm a new rust developer. I'm a University student, and I'm trying to branch out to languages outside of the C-sphere that universities tend to be stuck in.

Coming from a C++ background, I am finding the ownership rules of rust the hardest to manage.

I have two methods:

fn event_handler(&mut self){
    for event in self.event_pump.poll_iter(){
        self.is_running = self.event_window(event);
    }

}

and

fn event_window(&self, event: sdl2::event::Event)-> bool {
    let mut running = self.is_running;
    match event {
     sdl2::event::Event::Quit{ .. } => running = false,
     _ => {},
    }
    return running;
}

I receive the error

C:/Users/plays/.cargo/bin/cargo.exe run --package Game_Prototype --bin Game_Prototype
Compiling Game_Prototype v0.1.0 (G:\ProgrammingModding\GameEngine2D)
error[E0502]: cannot borrow *self as immutable because self.event_pump is also borrowed as mutable
--> src\game_context.rs:26:31
|
25 | for event in self.event_pump.poll_iter(){
| --------------- - mutable borrow ends here
| |
| mutable borrow occurs here
26 | self.is_running = self.event_window(event);
| ^^^^ immutable borrow occurs here

I understand why this error occurs. event_window() receives an immutable reference to self, whereas self.event_pump.poll_iter() is a mutable reference, and you cannot have a mutable reference borrow before an immutable reference borrow.

I'm not sure how I'm supposed to structure these methods in the way that rust's ownership rules would like me to, however. I tried changing event_window() to receive a mutable reference, but rust does not allow for two mutable borrows, either.

event_window() is supposed to iterate over the event queue of my program, and then test each given event for a list of event types, each split into their own functions for readability. (I.e., window events will have their own function, and key press events will have their own function)

How is one supposed to create methods that act upon the properties of a given class if the self keyword borrows the object, and then owns that object for the rest of the scope? It restricts me to acting upon only one property at a time, which seems horribly inefficient in terms of my work load..

I think it will be more fruitful to think of &mut T as a unique reference, and &T as a shared reference. It makes it easier to understand what the compiler is thinking.

In this case would just inline event_window.

fn event_handler(&mut self) {
    for event in self.event_pump.poll_iter() {
        if let sdl2::event::Event::Quit{ .. } = event {
            self.is_running = false;
        }
    }
}

The problem is that a function can't declare that it only uses certain fields, so Rust assumes that the entire value is borrowed. But inlining it makes the relationships clear to Rust.

1 Like

Inlining it fixes the problem, but as I expand the function to handle more events, it will become a long, complicated, and messy function.

Getting used to how rust handles ownership and structuring my programs around that is going to take some serious time.

Regardless, this helped a lot. Thank you!

1 Like

What you can do to handle this is keep the match in the for loop, and break out the parts you need to into private sub-functions. These functions should only take what they need as arguments (so if you only need field foo of Bar then don't pass bar, pass bar.foo)

use sdl2::event::Event;

fn private_helper(foo: Foo, bar: &Bar, qaz: &mut Qaz) {
    // long function
}

for event in self.even_pump.poll_iter() {
    match event {
        // for short parts, just inline
        Event::Quit { .. } => self.running = false,

        // for long ones, break out into a private helper
        Event::OtherEvent { foo } => private_helper(foo, &self.bar, &mut self.qaz),

        // ... other events ...
    }
}

Yes, this is the new concept that Rust that no other mainstream language uses. It is going to take some time.

No problem! I love answering questions.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.