Why `use`-ing a trait with overlapping methods changes type inference result?

Viewed 69

I was practicing with interior mutability and ran into behavior I cannot explain. Consider the following code:

use std::rc::Rc;
use std::cell::{RefCell, RefMut};

let shared: Rc<RefCell<u8>> = Rc::new(RefCell::new(0));
let mut borrowed = shared.borrow_mut();
*borrowed += 1;

It compiles and borrowed type is inferred as RefMut<u8>.

However, if you add use std::borrow::BorrowMut;, the code does not compile:

error[E0368]: binary assignment operation `+=` cannot be applied to type `Rc<RefCell<u8>>`
 --> src/main.rs:9:5
  |
9 |     *borrowed += 1;
  |     ---------^^^^^
  |     |
  |     cannot use `+=` on type `Rc<RefCell<u8>>`

and borrowed type is inferred as &mut Rc<RefCell<u8>> (if I understand correctly).

I see that both RefCell struct and BorrowMut trait have borrow_mut method, but it's not clear to me how simply use-ing the trait can change what method the compiler chooses.

Likely I don't know some details how method resolution works in Rust compiler. Can anybody help to understand it and/or share pointers where such subtleties are explained?

1 Answers

Fortunately for me, I'm not the only one who is confused by this inherent vs trait methods dispatching rules: https://github.com/rust-lang/rust/issues/39232.

Discussion on the issue resulted into adding what I was looking for to rust-lang reference which basically lists the steps performed by compiler to figure out which method to call. To summarize, Rc type is looked at before RefCell due to auto-dereference rules.


EDIT #1: (from @eggyal)

Also, BorrowMut is implemented for Rc whereas reaching RefCell::borrow_mut requires that shared first be dereferenced, which has lower priority—so when BorrowMut is in scope, that's where you end up. You can of course explicitly dereference to still get the original behaviour: (&*shared).borrow_mut() or shared.deref().borrow_mut() (the latter requires that std::ops::Deref be in scope).

Related