No problem : - )
I was just coding along, reproducing your example in the playground, and those two fields suddently had me going back to the struct definitions and adding new stuff. Their appearance just surprised me, nothing more.
No worries, it isn’t my first language either. The ability to phrase questions about Rust, especially about lifetimes, in an understandable manner of course correlates to how much one already understands about the topic. I wouldn’t blame your language skills but just the fact that Rust’s ownership system is hard and it’s even hard to ask questions about it ^^
Anyways... let me try to tackle your question again now:
So to be clear on the first, simple question, yes; calling a &self method on a &mut T is possible in general. I will try to explain the problem with your example involving may_double in the following sections:
After assigning hand in
fn do_player_turn(&mut self, i: usize) {
let hand = &mut self.hands[i];
loop {
// ...
self.context.may_double = self.may_double(hand);
// ...
}
}
the situation looks about as follows
───┬ some value of type `Round` higher up on the stack
└───┬ exclusively borrowed by self: &mut Round
└───┬ (implitly) exclusively re-borrowed (as &mut Round)
│ for indexing operation `hands[i]`
└──── the indexing returns a &mut Hand which got assigned
to `hand`; the &mut Hand requires the implicit &mut Round
re-borrow to stay alive due to the signature (*) of `index_mut`
so that this &mut Hand exclusively re-borrows the &mut Round
(*) signature of `index_mut`:
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
i.e.
fn index_mut<'a>(&'a mut self, index: Idx) -> &'a mut Self::Output;
if we want to pass hand to a method requiring a &Hand, we can do this as follows
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
└───┬ (implicit) &mut Round (exclusive re-borrow)
└───┬ hand: &mut Hand (exclusive borrow)
└──── (implicit) re-borrow of `hand` as &Hand (not exclusive)
Re-borrowing hand can also happen multiple times, so something like
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
└───┬ (implicit) &mut Round (exclusive re-borrow)
└───┬ hand: &mut Hand (exclusive borrow)
├──── (implicit) re-borrow of `hand` as &Hand (not exclusive)
└──── another re-borrow of `hand` as &Hand
or a copy of the previous re-borrow (not exclusive)
is okay (this is what I mean by "not exclusive"). However for calling a &self method on self, we need to do this:
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
└──── (implicit) re-borrow of `self` as &Round (not exclusive)
which is only allowed once the previous, exclusive re-borrow is gone. Both in parallel, like this is not allowed:
// not allowed!!
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
├───┬ (implicit) &mut Round (**EXCLUSIVE** re-borrow)
│ └───┬ hand: &mut Hand (exclusive borrow)
│ └──── (implicit) re-borrow of `hand` as &Hand (not exclusive)
└──── (implicit) re-borrow of `self` as &Round (not exclusive,
but collides with the EXCLUSIVE re-borrow further up)
But this is what would be needed for
self.may_double(hand);
^^^^ ^^^^
│ └─ the re-borrow of `hand` as &Hand
└─ the re-borrow of `self` as &Round
This borrowing tree is not allowed since the two brances split at a point where the top one is an “exclusive”, i.e. mut borrow, which means there can be no such splitting at this level of the tree.
In the
/* a */ v_ref.push(42);
/* b */ println!("size = {}", do_something_immut(&v_ref));
/* c */ v_ref.push(42);
example, you instead have
───┬ Vec<i32> value
└───┬ v_ref: &mut Vec<i32> (exclusive borrow)
└──── (implicit) &mut Vec<i32> (exclusive re-borrow)
for the call /* a */, then
───┬ Vec<i32> value
└───┬ v_ref: &mut Vec<i32> (exclusive borrow)
└──── re-borrow of `v_ref` as &Vec<i32> (not exclusive)
for the call /* b */ (the exclusive re-borrow of v_ref already ended after /* a */), and finally
───┬ Vec<i32> value
└───┬ v_ref: &mut Vec<i32> (exclusive borrow)
└──── (new, implicit) &mut Vec<i32> (exclusive re-borrow)
again for call /* c */.
The main takeaway is that in the may_double example, while the compiler knows that hand comes from self (as a re-borrow), it cannot have an immutable use of hand sort-of “propagate down” the re-borrow tree to retroactively make all the borrows this builds upon be immutable. On the other hand, if you use let hand = &self.hands[i]; instead, then the situation looks like this to begin with:
───┬ some value of type `Round` higher up on the stack
└───┬ exclusively borrowed by self: &mut Round
└───┬ (implitly) re-borrowed self as &Round (not exclusive)
│ for indexing operation `hands[i]`
└──── the indexing returns a &Hand which got assigned
to `hand`; the &Hand requires the implicit &Round
re-borrow to stay alive due to the signature (*) of `index`
so that this &Hand re-borrows the &mut Round
(but not exclusively re-borrows it)
(*) signature of `index`:
fn index(&self, index: Idx) -> &Self::Output;
i.e.
fn index<'a>(&'a self, index: Idx) -> &'a Self::Output;
This means that now, something like this is possible:
// allowed
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
├───┬ (implitly) re-borrow of `self` as &Round (not exclusive)
│ └──── hand: &Hand (not exclusive)
└──── (implicit) re-borrow of `self` as &Round (not exclusive)
for a call to self.may_double(hand). The variable hand does not need to be re-borrowed since re-borrows are only necessary for mutable references (immutable ones can be passed to e.g. may_double by copying the reference). This borrowing tree is allowed since the two brances split at a point where neither node is an “exclusive”, i.e. mut borrow.
For some further reading there is this great post on github that has this chapter covering pretty much this very topic of “downgrading” a mutable reference and why it doesn’t have the desired effect.
Additional note: I was ignoring the modification of .context in my diagrams above. To understand why that is okay, too, one would need to consider that actually Rust allows multiple (even mutable) re-borrows of self: &mut Self into references to individual fields to happen in parallel as long as they are accessing different fields. This is why .context can be mutated while the shared reference in hand, reborrowing the .hands field of *self still exists.
Edit: Here’s some diagrams for the full picture:
self.context.may_double = self.may_double(hand);
Initial situation:
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
└───┬ (implitly) re-borrow of `self.hands` as &ArrayVec<[Hand; 32]> (not exclusive)
│ (ONLY BORROWS the `.hands` field)
└──── hand: &Hand (not exclusive)
First step: evaluating the right-hand-side self.may_double(hand)
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
├───┬ (implitly) re-borrow of `self.hands` as &ArrayVec<[Hand; 32]> (not exclusive)
│ │ (ONLY BORROWS the `.hands` field)
│ └──── hand: &Hand (not exclusive)
└──── (implicit) re-borrow of `self` as &Round (not exclusive)
(the re-borrows of self and self.hands overlap but both are not exclusive)
Second step: modifying self.context.may_double
───┬ Round value
└───┬ self: &mut Round (exclusive borrow)
├───┬ (implitly) re-borrow of `self.hands` as &ArrayVec<[Hand; 32]> (not exclusive)
│ │ (ONLY BORROWS the `.hands` field)
│ └──── hand: &Hand (not exclusive)
└──── (implicit) re-borrow of `self.context.may_double` as &mut bool (exclusive)
(ONLY BORROWS the `.may_double` field of the `.context` field)
(the re-borrow of self.context.may_double is exclusive but does refer to a different field than the re-borrow of self.hands, so it’s okay)