rustsmart-pointersborrow

Why is importing Borrow making calling to borrow to be Borrow::borrow instead of RefCell::borrow


I was mistakenly imported std::borrow::Borrow and got stuck with the error message of the following snippet:

use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;

fn next(current: Rc<RefCell<i32>>) {
    let m = (*current).borrow().abs(); //ok
    let l = current.borrow().abs(); //error
}

Playground

The problem is manual dereference works, but deref coercion does not seem to:

error[E0282]: type annotations needed
 --> src/lib.rs:7:21
  |
7 |     let l = current.borrow().abs(); //error
  |                     ^^^^^^   --- type must be known at this point
  |
help: try using a fully qualified path to specify the expected types
  |
7 |     let l = <Rc<RefCell<i32>> as Borrow<Borrowed>>::borrow(&current).abs(); //error
  |             ++++++++++++++++++++++++++++++++++++++++++++++++    

If removing the use std::borrow::Borrow; it works as expected:

let l = current.borrow().abs(); //ok

This behavior looks confusing. I thought that Rc: Borrow is satisfied so there should not be any differences.

The duplicated question does answer why is dereference required. It specifies exactly what I mentioned in the original post.


Solution

  • The culprit is this blanket implementation of Borrow:

    impl<T: ?Sized> Borrow<T> for T {
        fn borrow(&self) -> &T {
            self
        }
    }
    

    If this impl is in scope (through use std::borrow::Borrow;), your call to .borrow() is ambiguous, as Rc<T> implements Borrow<T> itself.

    As the compiler says, depending on the type of l, a different impl should be called:

    fn next(current: Rc<i32>) {
        let m = (*current).borrow(); //ok
        // both of these are valid!
        let l_1: &Rc<i32> = current.borrow(); // uses `impl Borrow<T> for T` 
        let l_2: &i32 = current.borrow();     // uses `impl Borrow<T> for Rc<T>`
    }
    

    Playground (I replaced Rc<RefCell<i32>> with Rc<i32> in your example, as it is a bit simpler but exhibits the same behavior. )

    The reason it is not ambiguous if you Deref first (*current), is that the resulting type i32 once again only has one impl for Borrow.