如何使用包含在`RefCell`中的`BorrowMut`? [英] How to use `BorrowMut` contained within `RefCell`?

查看:75
本文介绍了如何使用包含在`RefCell`中的`BorrowMut`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am a big fan of BorrowMut as it allows me to provide APIs that allow to take ownership of arguments, or to take references as well. This makes them easier to use, but a little harder to implement for me - an acceptable tradeoff as the needs of the many outweigh the needs of the few :).

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

Now I am trying to use BorrowMut with RefCell and fail as borrow_mut() is implemented both by RefMut as well as BorrowMut. However, RefMut will take precedence, preventing my to drill down to the actual value contained within BorrowMut.

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

The following code allows you to reproduce the issue - the goal is to call doit() on the Client type:

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

推荐答案

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

You’re running into an unfortunate side-effect of auto-borrowing rules where what is almost always what is wanted (and almost always unambiguous) has wound up being ambiguous for this particular case and it’s chosen what to you is the wrong way. If you pull apart the expression and inspect the type at each step it becomes more obvious.

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

The first borrow_mut is interpreted as a method on RefCell which yields the core::cell::RefMut<'_, C> that is desired. (The fact that it has such an intrinsic method overrides the fact that it could otherwise construct a borrow_mut call by automatically taking a reference to self.hub.client, if self was in a mutable slot, as is shown later in this answer.) The problem is that the second one isn’t calling the borrow_mut of the BorrowMut implementation that you want.

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

There are two things that can happen at this stage when you want to call a method: automatic taking of references and automatic dereferencing. In this particular case, both of these yield a borrow_mut method that you can call:

  • 如果它接受对 RefMut 的可变引用,则它具有 &mut RefMut<'_, C>&mut T 实现了作用域内 trait BorrowMut,它提供了一个方法 borrow_mut,所以你只需要另一个 &mut RefMut<'_,C>,这巩固了它作为使用的选择.

  • If it takes a mutable reference to the RefMut, then it has &mut RefMut<'_, C>, and &mut T implements the in-scope trait BorrowMut which provides a method borrow_mut, and so you just get another &mut RefMut<'_, C>, which solidifies this as the choice to use.

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

If it dereferences the RefMut then it can get at a C, which it can then use the BorrowMut<Client> implementation of to satisfy the requested borrow_mut method call, yielding a &mut Client.

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

Which does it take? I’m not sure if the rules are defined anywhere (though they definitely will need to be soon if they are not), but what one can observe happening is that the first path is taken: it tries autoreferencing before it tries dereferencing, and so ref_cell_borrow.borrow_mut() returns a &mut RefMut<'_, C> rather than a &mut Client.

如果你想让它使用其他行为,你需要明确地取消引用 RefMut;那么自动获取可变引用只能在 C 处获取,这正是您所需要的.

If you wish to get it to use the other behaviour, you need to explicitly dereference the RefMut; then the automatic taking of a mutable reference can only get at the C, which is what you need.

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

Here’s the rough expansion with type annotations of what it needs to be; you can play around with things like assigning the type () to examine the error messages when you compile it:

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

Back in compact form, it’s (*self.hub.client.borrow_mut()).borrow_mut().doit().

这篇关于如何使用包含在`RefCell`中的`BorrowMut`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆