1

This question is not about why the compiler gives these errors — I think I understand that. This question is about how to manage the situation on the mutable path while hoping to be able to call other functions.

This is a simplification of what I am trying to do. I have a lot of Foos inside World and I need to update them based on other data from World. I have several private functions inside World that I would like to be able to call.

pub struct Foo {
    id: i32,
    //...
}

pub struct Bar {
    //...
}

pub struct World {
    foos: Vec<Foo>,
    bars: Vec<Bar>,
    //...
}

impl World {

    pub fn create() -> World {
        World {
            foos: Vec::new(),
            bars: Vec::new(),
        }
    }

    pub fn update(&mut self) {
        for foo in self.get_foos_mut() {
            //error[E0XXX]: cannot borrow `*self` as mutable/imutable ...
            let _bar = self.get_bar_given_foo(foo);
            //alter foo based on bar and other world things calling other world functions...
            //...
        }
    }

    //some useful utility functions that should be called several times
    //from different World places...

    fn get_foos_mut(&mut self) -> &mut [Foo] {
        //select slice interval based on foos or any other calculation using self data...
        &mut self.foos[..]
    }

    fn get_bar_given_foo(&self, foo: &Foo) -> &Bar {
        //select bar based on foo or any other calculation using self data...
        &self.bars[foo.id as usize]
    }

    //other utility functions that abstract e sequence of operations that I would 
    //like to use on the update path...    
}

fn main() {
    let mut world = World::create();
    world.update();
}

You can also run the code.

It looks a very limiting situation because when on a mutable path like the update function, I am unable to call any of the private methods from self.

Since I don't want to clone all the data, I could think of three solutions. I tried 1 and 2 and both work.

  1. Inline everything. Since this solution works for the example above, it looks like the compilation error is caused by the limitation of the Rust compiler that as I understand: it just looks the function signature and not the implementation of the functions.

  2. Create functions that receive references to what they need. There is no need borrow self again. The function World::get_foos_mut(&mut self) -> &mut [Foo] could be removed and created a new one outside World: fn world_get_foos_mut(foos: &mut Vec<Foo>) -> &mut [Foo] The code on the playground shows this solution.

  3. Create macros instead of regular functions? Since they are macros I suspect that there won't be any borrow involved. Since I am not familiar with macros I am unable to tell if this works or how to do this yet.

I really need other options because Solution 1 is not viable in my opinion and solution 2 looks very cumbersome and fragile (it looks that I might have the same problems but with the references inside World).

I am really frustrated about not being able to find the correct model to handle this.

Will the Rust compiler try to analyze these situations in the future?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
matias.g.rodriguez
  • 849
  • 11
  • 14
  • My recommended solution from the duplicates: [create more structs](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2153a80f000de253a91452c2eb2abb56). Effectively the concept of moving the functions out of `World` but putting them into new structs instead of being bare. – Shepmaster Jun 08 '19 at 01:53
  • For what it's worth, all 3 of your solutions are the same: The compiler can know that fields of a struct are disjoint and wont overlap, but only inside of a single function. This is deliberate and not a thing to be "fixed" in the future (as discussed in the duplicates). Inlining code (using a macro or not) moves it into a single function, so the compiler can validate the code. Creating fine-grained functions does the same in the other direction: if you only pass in a part of your struct, there's no way that other fields could be modified. – Shepmaster Jun 08 '19 at 01:57
  • Thanks @Shepmaster, I appreciate your time for pointing out the duplicates and suggesting me the solution in create_more_structs. I will need some time to go through all of this. – matias.g.rodriguez Jun 08 '19 at 02:07
  • You are quite welcome. I commend you for correctly identifying the core "workaround". I'm a Rust fanboy, but I honestly think that the smaller structs that I create in these cases helps my code have a better structure. – Shepmaster Jun 08 '19 at 02:18

0 Answers0