Inexplicable compiler borrow (beginner) error

Why does the below link does not compile with the line marked:
// DOES *NOT* COMPILE
While - if we replace it with the line above that, which in my beginners view does 100% the same - does compile without problem?

How should I rewrite that get_field2_as_mut() function to exactly do what I want in line 1.
I understand ownership a bit but don't understand what is the difference here.

1 Like

The compiler analyses one function at a time, so when it gets to do_something, it sees this:

fn do_something(&mut self)
{
    // let f: &mut i32 = &mut self.field2; // DOES COMPILE
    let f: &mut i32 = self.a_name_i_dont_care_about(); // DOES *NOT* COMPILE

    self.field1 = 42;
    *f = 43;
}

// The compiler does not look into other function bodies, it only considers signatures
// ... and it obviously do not understand english names
fn a_name_i_dont_care_about(&mut self) -> &mut i32
{
    some_code_i_dont_care_about_now!()
}

As you can see, there is no way to infer that self.field1 and f point to distinct objects.

I can understand that kind of. But that makes making little helper functions - like a small lookup - quite impossible?

In the method get_field2_as_mut, this method uses a &mut self, so the compiler assumes that self has been borrowed when reassigning field1, and multiple mutable references are not allowed, so there is no way to assign field1.

Others may be able to say more, but

yes, that is basically correct.

However that does not seem to be too big problem. If you want something simple, you can just access the field directly (you may need to refactor your struct to allow this -- that's maybe unfortunate). If you want something deep and complex, it would be too hard to determine which parts are borrowed anyway. If you really need multiple parts borrowed at the same time, you can do something like this[1]:

fn get_fields_mut<'a>(&'a mut self) -> (&'a mut i32, &'a mut i32) {
    (&mut self.field1, &mut self.field2)
}

  1. maybe lifetimes can be elided, I am not sure about elision rules in this case ↩ī¸Ž

yes, that is basically correct.
:slight_smile:

In reality the function is a lookup by index in some vector.
I want to prevent calling the function again and again and looking up the index.
So I presume an unsafe pointer is the solution I need.

It would be better to give a more specific use case, since in your use case the references to field1 and field2 are interchangeable; also, the lookup method that doesn't need to read what you need to modify, say, field2, you could try to pass the mutable references to the fields directly, instead of passing the whole structure.

However, if you need to read & write both field1 and field2 (in your lookup function), then mutable reference conflicts do exist.

... or, better said, it is well possible and easy to make them (as you did), but it is hard to use them as easily as direct field access.

A reproductable compilable use-case is a bit complicated. But imagine a chess program in the middle of a recursive long search routine, needing access to a specific vec-item in a few places.

Imagine something like this:

// start
let stackentry: *mut StackEntry = 
    unsafe { self.stack.as_mut_ptr().offset((self.depth() + 2) as isize) };
// lots of code
(*stackentry).in_check = true;
// lots of code
(*stackentry).pv_move = bestmove;

That just works... Now I am disappointed that it seems not doable in safe code with an easy mutable reference.

@ericlangedijk I would recommend attempting alternative designs, like returning multiple mutable borrows for disjoint fields from your reusable method like it was mentioned earlier. It adds some "unnecessary" coupling, but reaching for unsafe first when wanting multiple mutable access is not ideal. unsafe is indeed needed, like if you look at the implementation of Vec::split_at_mut, but it is best to provide small methods that will ensure the disjointness that you seek. Given your description there it sounds like split_at_mut is exactly what you might be looking for.

4 Likes

In general we won't need to maintain a mutable reference under normal circumstances, after all it could possibly be done with (easily optimized) literal closures; my suggestion would be to try to think about your whole architecture, there might be some more rusty (rather than more, say, csharp-like) solutions.

I generally agree with that.
Regarding architecture: chessprograms are a bit different.
If you look at the search routines of (for example) Stockfish you can see that. Basically an incredible mess with pointers pointing all over the place.
In a 'normal' environment I would not have this problem so explicit, I think.

1 Like

Ah, chessprogram. While I haven't tried to write this type of program, I've tried to do a lot of object simulators with rust, and it's a mess, too, full of RefCells and (hard to make sense of what it points to) pointers. I can't help much probably, as I'm struggling with this one too. But anyway, good luck.

Found a related article that may be of some help to you.

https://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/

Interesting! Somehow it feels like a 'fight' between the compiler and the programmer.
The programmer says: "I know what I am doing" and the compiler says: "I don't know what is happening exactly".