Newbie borrow issues with visitor pattern

Hi all, I'm writing a small game to help myself learn rust. I'm trying to do some basic event handling, but am being shot down by compiler for the following code:

trait PlatformHandler {
    fn on_mouse_down(&mut self);
}

struct Platform {}

impl Platform {
    fn pump_events(&mut self, handler: &mut dyn PlatformHandler) {
        // some code detected a mouse press, notify the handler
        handler.on_mouse_down();
    }
}

struct Engine {
    click_count: i32,
    platform: Platform,
}

impl Engine {
    fn new() -> Engine {
        Engine {
            click_count: 0,
            platform: Platform {},
        }
    }

    fn advance(&mut self) {
        // attempt to pass in visitor-esque reference to self each advance so that
        // we don't have a listener registration/unregistration observer system
        // as there will only be one listener
        self.platform.pump_events(self); // *** FAIL ***
    }
}

impl PlatformHandler for Engine {
    fn on_mouse_down(&mut self) {
        println!("mouse down!");
        self.click_count += 1;
    }
}

fn main() {
    let mut engine = Engine::new();
    loop {
        engine.advance();
    }
}

When I build this, I get the following error:

   Compiling foo v0.1.0 (/mnt/data/projects/foo)
error[E0499]: cannot borrow `self.platform` as mutable more than once at a time
  --> src/main.rs:30:9
   |
30 |         self.platform.pump_events(self); // *** FAIL ***
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^----^
   |         |                         |
   |         |                         first mutable borrow occurs here
   |         second mutable borrow occurs here
   |         first borrow later captured here by trait object

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:30:35
   |
30 |         self.platform.pump_events(self); // *** FAIL ***
   |         ------------- ----------- ^^^^ second mutable borrow occurs here
   |         |             |
   |         |             first borrow later used by call
   |         first mutable borrow occurs here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `foo` due to 2 previous errors

So the first mutable borrow to self.platform makes sense, great. I am then just passing along the already mutable reference to self into pump_events, which should be fine. I don't see where the second borrow happens, even with the error message underlining the code.

My belief is that now I have two borrows:

  • One mutable borrow to self
  • One mutable borrow to platform

What am I missing here, and how can I get this to work? Thanks for your time.

The borrow of self is also a borrow of self.platform because self contains self.platform. Note that if you have two separate mutable references, one &mut Engine and one &mut Platform, then the &mut Engine can be turned into a second mutable reference (&mut Platform) to the same platform, violating Rust's requirements of mutable references to be unique.

There's multiple approaches, and which is most useful depends a bit on the specifics of your code which seems like a simplified example. (Don't get me wrong, it's actually great that you provided a complete / self-contained code example together with your question for understanding the compiler behavior.)

One possible approach: If it's the case that the on_mouse_down implementation really only needs to access the non-Platform fields of Engine, you could either put those together in a dedicated struct

struct Engine {
    remaining_fields: OtherEngineFields,
    platform: Platform,
}


struct OtherEngineFields {
    click_count: i32,
}

(naming can be improved)

or you could create a helper-struct represending a mutable borrow of all the remaining (or all the necessary [for on_mouse_down]) fields.

struct Engine {
    click_count: i32,
    platform: Platform,
}


struct EnginePlatformHandler<'a> {
    click_count: &'a mut i32,
}

Then, in either approach, you can make that new struct implement PlatformHandler in order to make sure that the borrow in the PlatformHandler is disjoint from the Platform field:

trait PlatformHandler {
    fn on_mouse_down(&mut self);
}

struct Platform {}

impl Platform {
    fn pump_events(&mut self, handler: &mut dyn PlatformHandler) {
        // some code detected a mouse press, notify the handler
        handler.on_mouse_down();
    }
}

struct Engine {
    click_count: i32,
    platform: Platform,
}

struct EnginePlatformHandler<'a> {
    click_count: &'a mut i32,
}

impl Engine {
    fn new() -> Engine {
        Engine {
            click_count: 0,
            platform: Platform {},
        }
    }

    fn handler_and_platform(&mut self) -> (EnginePlatformHandler<'_>, &mut Platform) {
        let Engine {
            click_count,
            platform,
        } = self;
        let handler = EnginePlatformHandler { click_count };
        (handler, platform)
    }

    fn advance(&mut self) {
        let (mut handler, platform) = self.handler_and_platform();
        platform.pump_events(&mut handler);
    }
}

impl PlatformHandler for EnginePlatformHandler<'_> {
    fn on_mouse_down(&mut self) {
        println!("mouse down!");
        *self.click_count += 1;
    }
}

fn main() {
    let mut engine = Engine::new();
    for _ in 0..10 {
        engine.advance();
    }
}

(playground)


If splitting up the mutable borrow this cleanly doesn't work, there's also the option to use some interior mutability primitives such as Cell/RefCell in order to replace some of the &mut …s by & ….

Thank you for the prompt reply! My project is indeed more complicated than this, but I wouldn't want to force someone who is already taking the time to look at my broken code to also understand what is relevant to the problem and what is not :slight_smile:

The fact that ownership propagates downwards to child objects makes sense; indeed, if it didn't work like that, a lot of the protections provided by the language would go out of the window. It sounds like rust really wants to work like a uni-directed graph, and adding child objects to handle specific behaviors is an emergent pattern; I guess having callee call back into caller is just not very idiomatic?

As an aside, this is pretty interesting syntax:

        let Engine {
            click_count,
            platform,
        } = self;

I'm guessing that it de-structures self and moves the members into similarly named locals? I was trying to do this to validate that this is shorthand, but apparently it's actually different as this causes compiler errors:

        let click_count = self.click_count;
        let platform = self.platform;

It's a short-hand in multiple ways:
First, it uses punning for the field names, i.e.

        let Engine {
            click_count,
            platform,
        } = self;

is equivalent to

        let Engine {
            click_count: click_count,
            platform: platform,
        } = self;

and the pattern name: name using the same name twice can be abbreviated. Second, since self is a mutable reference, i.e. &mut Engine, this pattern uses so-called "ergonomics" (or "match ergonomics") so that the code above is equivalent to

        let &mut Engine {
            click_count: ref mut click_count,
            platform: ref mut platform,
        } = self;

i.e. the pattern de-references the &mut Engine (through the &mut ... pattern right after let) and then binds all the fields by mutable reference instead of by-value (through the ref mut patterns).

Instead of a dereferencing pattern, one can also use a dereferencing expression (a "*" used on the other side of the =), so that

        let &mut Engine {
            click_count: ref mut click_count,
            platform: ref mut platform,
        } = self;

is equivalent to

        let Engine {
            click_count: ref mut click_count,
            platform: ref mut platform,
        } = *self;

This is equivalent to the sequence of assignments

let ref mut click_count = (*self).click_count;
let ref mut platform = (*self).platform;

or

let click_count = &mut (*self).click_count;
let platform = &mut (*self).platform;

or more commonly written as

let click_count = &mut self.click_count;
let platform = &mut self.platform;

(as field-accessor expressions like self.click_count can implicitly dereference their left operant (self))


So year, as explained above, it doesn't really "destructure" the struct in the sense that it takes ownership, instead it splits up the mutable reference to the whole struct into mutable references to each of the fields.

2 Likes

Makes sense! I am sort of limping along through this, and I didn't even know what exactly ref did before I saw your post, hehe. Thanks again for the help. It's actually somewhat reassuring to know that this is not a simple "tag with keyword X, duh!" kind of solution that I missed, and this actually requires a little bit of a dance to work :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.