Skip to content

Collision of Borrow::borrow() and RefCell::borrow() #41906

@gavento

Description

@gavento

The trait std::borrow::Borrow and type std::rc::RefCell both have a method borrow(). This leads to ambiguity, possibly hard-to-understand errors and most importantly breaking unrelated code by adding use std::borrow::Borrow;.

Consider the following code:

use std::rc::Rc;
use std::cell::RefCell;
// Uncommenting causes compile error
//use std::borrow::Borrow;

pub struct S {
	flag : bool
}

type SCell = Rc<RefCell<S>>;

fn main() {
    // Type annotations just for clarity
    let s : SCell = Rc::new(RefCell::new(S {flag: false}));
    let sb : &S = &s.borrow();
    println!("{:?}", sb.flag);
}

(Rust playground link)

This code compiles and works as intended. But when you add use std::borrow::Borrow;, the compiler complains:

rustc 1.17.0 (56124baa9 2017-04-24)
error[E0277]: the trait bound `std::rc::Rc<std::cell::RefCell<S>>: std::borrow::Borrow<S>` is not satisfied
  --> <anon>:13:21
   |
13 |     let sb: &S = &s.borrow();
   |                     ^^^^^^ the trait `std::borrow::Borrow<S>` is not implemented for `std::rc::Rc<std::cell::RefCell<S>>`
   |
   = help: the following implementations were found:
             <std::rc::Rc<T> as std::borrow::Borrow<T>>

The problem is that the method call s.borrow() is applied to Rc which is an instance of Borrow rather than to the contained RefCell as intended (and as working in the example above), and the error just complains that this borrow is not possible (which is fine).

This is mentioned by @spirali in related #41865, but this issue deals with a different part of the problem. (Namely that if you remove the type annotation from let sb : &S = ..., the error message will complain about ambiguity of the Borrow::borrow() return type in a confusing way.)

I would propose to change the method name for RefCell::borrow to something else (a painful change, but leaving such bugs around things implementing the Deref trait might be even worse for the language long-term) and avoid this naming collisions whenever possible (is there a guideline for that?). Any other solutions?

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions