Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call field's function and pass &self in Rust

I have a MainStruct which owns an instance of HelperStruct. I want to make a method that calls helper's method. But the helper's method really needs (immutable) access to MainStruct

struct MainStruct {
    helper: HelperStruct,
}

impl MainStruct {
    pub fn call_helper(&mut self, args) { 
        // &mut self because the_method changes inner state of HelperStruct
        self.helper.the_method(self, args)
    }
}

impl HelperStruct {
    pub fn the_method(&mut self, owner: &MainStruct, args) {
        owner.other_method();
    }
}

With this setup, I'm getting

error[E0502]: cannot borrow `self.helper` as mutable because it is also borrowed as immutable
214 |         self.helper.the_method(self, args)
    |         ^^^^^^^^^^^^----------^----^^^^^^^^^^^^^^^^^^^^^^
    |         |           |            |
    |         |           |            immutable borrow occurs here
    |         |           immutable borrow later used by call
    |         mutable borrow occurs here

It is crucial that the MainStruct remains the primary interface for interaction with the functionality (so having Helper own Main isn't an option).

How should I restructure my code (or maybe use smart pointers?) so that I can interact with the owner from the inside of Helper? Also, it is guaranteed that the_method() will only be called inside an instance of MainStruct.

Actual context:

The MainStruct is the Field of my game. The HelperStruct is the class that takes care of pathing, A* mostly. A* has a number of large arrays that store its state. After an A* pass only some of them need to be reinitialized (or otherwise cleaned).

I end up calling A* a lot of times, so I want to avoid fully reinitializing it for every pass. So I store it in Field, and only call reset() after a pass.

To path plan properly, A* needs to look at the Field (obstacles, etc). While the A* is going, it of course changes some of its own fields, so the_method, being actually

pub fn full_pathing(&mut self, field: &Field, source: (i32, i32), destination: (i32, i32))

needs to have a &mut self

like image 480
Mikhail Vlasenko Avatar asked Aug 09 '26 09:08

Mikhail Vlasenko


1 Answers

You can make the_method an associated function, and have it only receive a &mut MainStruct. It will have to access itself through that reference:

struct MainStruct {
    helper: HelperStruct,
}

impl MainStruct {
    pub fn call_helper(&mut self, args) { 
        HelperStruct::the_method(self, args)
    }
}

impl HelperStruct {
    pub fn the_method(main: &mut MainStruct, args) {
        main.other_method();

        //Access helper like this:
        main.helper.whatever();
    }
}

Alternatively, if your two structs are so intertwined, you could combine them into a single struct.

like image 87
FZs Avatar answered Aug 12 '26 03:08

FZs