The first and foremost thing to understand is that function signature declares an interface - it's not just a hint. This is critical to understand. The interface of all of update
, update_1a
, and update_1b
is that they mutably borrow the entirety of Field
for the duration of the routine.
When you are borrowing a structure mutably, you are then free to borrow any of the sub-elements mutably. HOWEVER, those sub-borrow imply a transitive borrow on the parent structure - after all, maintaining that borrow may be essential to enforcing certain invariants between fields of the struct.
So, take a look at update_1b
. Its signature is asking for it to mutably borrow the entirety of self. However, update_1a
cannot call update_1b
in the way it does since a mutable borrow must be exclusive, and you're attempting to simultaneously borrow the players
part of the struct and the entire struct exclusively.
Many have tried to argue against this - that a sufficiently smart compiler should be able to "see" that update_1a
's usage of update_1b
is legal since update_1b
is only borrowing the ball
field. However, it's not this simple - if ball
and players
need to be kept in sync in some way, especially to enforce some safety-critical property the compiler can't see, then it would be illegal for the compiler to allow this.