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

标签 rust

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

现在我尝试将 BorrowMutRefCell 一起使用,但失败,因为 borrow_mut() 都是由 RefMut 实现的> 以及 BorrowMut。但是,RefMut 将优先,阻止我深入了解 BorrowMut 中包含的实际值。

示例

以下代码允许您重现该问题 - 目标是在 Client 类型上调用 doit():

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

最佳答案

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

第一个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() .

关于rust - 如何使用 `BorrowMut` 中包含的 `RefCell` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29074573/

相关文章:

python - 在包含来自不同 crate 的类型的使用rust 结构上使用pyo3 pyclass

javascript - 如何使用CryptoJS进行加密以及如何使用Rust Magic Crypt进行解密

rust - 为什么 !Send 在 .await 之前被 drop() 丢弃的值意味着 Future 是 !Send?

Rust:如何限制派生特征的类型参数

rust - 为什么 Serde 默认不支持 Rc 和 Arc 类型?

rust - 为什么 [u8] 没有实现 Clone?

rust - 我可以在泛型函数中使用的只有原始类型的特征吗?

rust - 是否可以为另一个项目中的库定义一个数组长度常量?

vim - 如何在 Rust 程序的 `main` 函数处打开 Vim?

user-interface - 在 GTK3 (gtk-rs) 中用于像素绘制的 Canvas