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

时间:2015-03-16 10:30:56

标签: 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();
}

这会产生以下错误:

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()

你能说出我打电话的方式吗?有可能吗?

的元

✗ 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

1 个答案:

答案 0 :(得分:3)

你遇到了一个不幸的自动借用规则的副作用,其中几乎总是想要的东西(并且几乎总是明确的)已经结束了对这个特定情况的模棱两可而且选择了你的错误是什么办法。如果你拆开表达式并检查每一步的类型,它就会变得更加明显。

第一个borrow_mut被解释为RefCell上的方法,它产生所需的core::cell::RefMut<'_, C>。 (如果borrow_mut位于可变插槽中,那么它具有这种内在方法的事实优先于通过自动引用self.hub.client来构建self调用的事实。这个答案稍后会显示。)问题是第二个没有调用你想要的borrow_mut实现的BorrowMut

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

  • 如果它需要RefMut的可变引用,那么它有&mut RefMut<'_, C>,而&mut T实现范围内的特征BorrowMut,它提供了一种方法borrow_mut,因此您只需获得另一个&mut RefMut<'_, C>,这会将其固定为使用选项。

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

需要哪些?我不确定规则是否在任何地方定义(虽然它们肯定会很快就会很快),但是可以观察到的是第一条路径:它在尝试解除引用之前尝试自动引用,等等ref_cell_borrow.borrow_mut()会返回&mut RefMut<'_, C>而不是&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();

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