0
votes

After learning Rust for a while, I started to think that I understood its ownership/borrowing mechanism, but the next example makes me really puzzled. I am playing with rust-sdl2:

extern crate sdl2;

use sdl2::Sdl;
use sdl2::event::Event;
use sdl2::render::Renderer;

struct SdlDriver<'a> {
    sdl_ctx  : Sdl,
    renderer : Renderer<'a>
}

impl<'a> SdlDriver<'a> {
    fn event_pump(&self) -> sdl2::event::EventPump {
        self.sdl_ctx.event_pump()
    }

    fn draw_rect(&mut self, x: i32, y: i32) {
        let mut drawer = self.renderer.drawer();
        drawer.draw_rect(sdl2::rect::Rect::new(x-20, y-20, 40, 40));
        drawer.present();
    }

    fn event_loop(&mut self) {
        let mut event_pump = self.event_pump();

        loop {
            let event = event_pump.wait_event();

            match event {
                Event::Quit {..} => { break },
                Event::MouseMotion { x, y, .. } => { 
                    self.draw_rect(x, y);
                },
                _ => ()
            }
        }
    }
}

fn main() {
}

The compiler shows me the error:

main.rs:32:25: 32:29 error: cannot borrow `*self` as mutable because it is also borrowed as immutable
main.rs:32                         self.draw_rect(x, y);
                                   ^~~~
main.rs:24:34: 24:38 note: previous borrow of `*self` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `*self` until the borrow ends
main.rs:24             let mut event_pump = self.event_pump();
                                            ^~~~
main.rs:37:10: 37:10 note: previous borrow ends here
main.rs:23         fn event_loop(&mut self) {
...
main.rs:37         }
                   ^

As I understand it, the call to self.event_pump() should temporarily borrow *self, return EventPump struct which is now owned by event_loop(), and then release *self. But it looks like *self is borrowed for the entire lifetime of event_pump. Why?

For comparison, when I call methods without returning results, like

self.draw_rect(x, y);
self.draw_rect(x, y);

everything works fine, *self is released immediately after every call.

Another thing I don't understand is why *self is borrowed (with star)? event_loop(&mut self) already borrows self from its caller, why is it required to borrow *self here?

How should the example above be rewritten to satisfy Rust's borrow checker?

1

1 Answers

1
votes

the call to self.event_pump should temporarily borrow *self

Yup

then return EventPump struct which is now owned by event_loop and release *self

Nope. This is your method:

fn event_pump(&self) -> sdl2::event::EventPump {
    self.sdl_ctx.event_pump()
}

Before lifetime elision, it would look like:

fn event_pump<'a>(&'a self) -> sdl2::event::EventPump<'a> {
    self.sdl_ctx.event_pump()
}

The EventPump struct looks like:

pub struct EventPump<'sdl> {
    _sdl:    PhantomData<&'sdl ()>,

    // Prevents the event pump from moving to other threads.
    // SDL events can only be pumped on the main thread.
    _nosend: PhantomData<*mut ()>
}

When you borrow self.sdl_ctx, you are also borrowing self. The borrow checker has fine-grained checks within a function, but not across function calls. Checking across calls would be very expensive, if it's even technically possible.

A way to work around this is to directly call methods on the member variables - instead of creating your own event_pump method, just inline it. Often this leads to creating finer-grained structs.