Okay: Playground.
I suppose this is because every type T
implements Borrow<T>
, so std::borrow::Borrow::borrow_mut
is chosen instead of std::cell::RefCell::borrow_mut
.
The exact order of method resolution is specified here in the reference:
[…]
When looking up a method call, the receiver may be automatically dereferenced or borrowed in order to call a method. This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call. The following procedure is used:
The first step is to build a list of candidate receiver types. Obtain these by repeatedly dereferencing the receiver expression's type, adding each type encountered to the list, then finally attempting an unsized coercion at the end, and adding the result type if that is successful. Then, for each candidate T
, add &T
and &mut T
to the list immediately after T
.
This wording is a bit confusing, but look at the last sentence: "Then, for each candidate T
, add &T
and &mut T
to the list immediately after T
."
Thus, the compiler will try borrow_mut
on the &mut Arc
first, which succeeds (with Borrow::borrow_mut
), before dereference kicks in (which would let you use RefCell::borrow_mut
).
I would like to add that deref-coercion still works when there is no disambiguity, e.g. as argument to a function:
use std::borrow::BorrowMut;
use std::cell::{RefCell, Ref, RefMut};
use std::sync::Arc;
struct Instr;
fn ref_cell_borrow_mut<'a, T>(ref_cell: &'a RefCell<T>) -> RefMut<'a, T> {
ref_cell.borrow_mut()
}
fn main() {
let tmp: Arc<RefCell<Instr>> = Arc::new(RefCell::new(Instr));
let _: RefMut<'_, Instr> = (*tmp).borrow_mut();
let _: RefMut<'_, Instr> = ref_cell_borrow_mut(&tmp); // we don't need `*` here
let _: RefMut<'_, Instr> = ref_cell_borrow_mut(&*tmp); // but we can use it
let _: Ref<'_, Instr> = tmp.borrow();
}
(Playground)
Here, deref-coercion happens when calling ref_cell_borrow_mut(&tmp)
.