如何使用包含在`RefCell`中的`BorrowMut`?

Byr*_*ron 2 rust

我是BorrowMut它的忠实粉丝,因为它允许我提供允许获取参数所有权或引用的 API。这使它们更容易使用,但对我来说实施起来有点困难——这是一个可以接受的权衡,因为许多人的需求大于少数人的需求:)。

现在我想使用BorrowMutRefCell和失败,因为borrow_mut()是通过实现双方RefMut以及BorrowMut。但是,RefMut将优先,防止我深入到 中包含的实际值BorrowMut

例子

下面的代码可以让您重现该问题-目的是调用doit()Client类型:

use std::cell::RefCell;
use std::borrow::BorrowMut;

struct Hub<C> {
    client: RefCell<C>
}

impl<C> Hub<C> 
    where C: BorrowMut<Client> {

    fn new(client: C) -> Hub<C> {
        Hub {
            client: RefCell::new(client)
        }
    }

    fn builder<'a>(&'a self) -> Builder<'a, C> {
        Builder {
            hub: self
        }
    }
}

struct Builder<'a, C: 'a> {
    hub: &'a Hub<C>
}

impl<'a, C> Builder<'a, C>
    where C: BorrowMut<Client> {
    fn use_client(self) {
        // 1: borrow_mut() of RefCell
        // 2: borrow_mut() of BorrowMut()
        // but doesn't work, as RefMut returned by 1) always yields RefMut
        self.hub.client.borrow_mut().borrow_mut().doit()
    }
}

struct Client;
impl Client {
    fn doit(&mut self) {
        println!("DID IT!!")
    }
}


// HUB USAGE
{
    let h = Hub::new(Client);
    h.builder().use_client();
}

{
    let mut c = Client;
    let h = Hub::new(&mut c);
    h.builder().use_client();
}
Run Code Online (Sandbox Code Playgroud)

这会产生以下错误:

tests/lang.rs:1076:55: 1076:61 error: type `&mut core::cell::RefMut<'_, C>` does not implement any method in scope named `doit`
tests/lang.rs:1076             self.hub.client.borrow_mut().borrow_mut().doit()
Run Code Online (Sandbox Code Playgroud)

你能指出我将如何拨打这个电话吗?有可能吗?

? rustc --version --verbose
rustc 1.0.0-nightly (3e4be02b8 2015-03-13) (built 2015-03-13)
binary: rustc
commit-hash: 3e4be02b80a3dd27bce20870958fe0aef7e7336d
commit-date: 2015-03-13
build-date: 2015-03-13
host: x86_64-apple-darwin
release: 1.0.0-nightly
Run Code Online (Sandbox Code Playgroud)

Chr*_*gan 5

您遇到了自动借用规则的不幸副作用,其中几乎总是想要的(并且几乎总是明确的)在这种特殊情况下变得模棱两可,并且它选择了对您来说是错误的方式。如果您将表达式分开并在每一步检查类型,它就会变得更加明显。

第一个borrow_mut被解释为RefCell产生core::cell::RefMut<'_, C>所需的方法。(它具有这样一个内部方法的事实覆盖了这样一个事实,即它可以borrow_mut通过自动引用对self.hub.client, 如果self在可变插槽中来构造调用,如本答案后面所示。)问题在于第二个不是调用您想要borrow_mutBorrowMut实现

当您想要调用方法时,在此阶段可能会发生两件事:自动获取引用和自动取消引用。在这种特殊情况下,这两种borrow_mut方法都会产生一个您可以调用的方法:

  • 如果它接受对 的可变引用RefMut,则它具有&mut RefMut<'_, C>,并&mut T实现了BorrowMut提供方法的范围内特征borrow_mut,因此您只会得到另一个&mut RefMut<'_, C>,这将其固化为使用的选择。

  • 如果它取消引用 theRefMut那么它可以得到 a C,然后它可以使用 的BorrowMut<Client>实现来满足请求的borrow_mut方法调用,产生 a &mut Client

需要哪个?我不确定规则是否在任何地方定义(尽管如果没有,它们肯定需要很快定义),但是可以观察到的情况是采用了第一条路径:它在尝试取消引用之前尝试自动引用,等等ref_cell_borrow.borrow_mut()返回 a&mut RefMut<'_, C>而不是 a &mut Client

如果您希望它使用其他行为,则需要明确取消引用RefMut; 那么自动获取可变引用只能获取C,这正是您所需要的。

这是带有类型注释的粗略扩展;您可以()尝试分配类型以在编译时检查错误消息:

let mut ref_cell_borrow: std::cell::RefMut<C> = self.hub.client.borrow_mut();
let client: &mut Client = (*ref_cell_borrow).borrow_mut();
client.doit();
Run Code Online (Sandbox Code Playgroud)

回到紧凑的形式,它是(*self.hub.client.borrow_mut()).borrow_mut().doit().