将 Option<RefCell<T>> 转换为 Option<&T>

Daz*_*ins 2 rust

我们知道将 type 值转换RefCell<T>为 type 值&T以用作函数中的参数很容易:

fn main() {
    let a: RefCell<i32> = RefCell::new(0);
    my_func(a.borrow().deref());
}

fn my_func(i: &i32) {}
Run Code Online (Sandbox Code Playgroud)

在我的场景中,RefCells 存储在 a 中HashMap,因此它们被包装在Option. 我还希望传递给它们的函数具有选项的概念,但我只想传递非可变引用,而不是整个RefCell. 我们可以这样实现:

fn main() {
    let a: Option<RefCell<i32>> = Some(RefCell::new(0));

    match a {
        Some(ref_cell) => my_func(Some(ref_cell.borrow().deref())),
        None => my_func(None)
    };
}

fn my_func(i: Option<&i32>) {}
Run Code Online (Sandbox Code Playgroud)

这是可行的,但在我的特定场景中,my_func将其中的几个Option<&T>作为参数,因此这样做意味着match每个参数都会嵌套并呈指数增长。因此,采取某种方式这样做会很有帮助:

fn main() {
    let a: Option<RefCell<i32>> = Some(RefCell::new(0));

    let c = match a {
        Some(ref_cell) => Some(ref_cell.borrow().deref()), // won't compile as this borrow won't live long enough
        None => None
    };

    my_func(c);
}

fn my_func(i: Option<&i32>) {}
Run Code Online (Sandbox Code Playgroud)

所以本质上我希望能够从 转换Option<RefCell<T>>Option<&T>. 我觉得这应该有可能,但我想不出办法。我总是遇到一些执行的问题,.borrow()RefCell它的寿命不够长。

kmd*_*eko 7

您可以使用以下方法来执行此操作Option

a.as_ref().map(RefCell::borrow).as_deref()
Run Code Online (Sandbox Code Playgroud)
  • as_ref()用于将 转换Option<RefCell<_>>为 aOption<&RefCell<_>>以避免消耗它。如果您已经有一个Option<&RefCell<_>>因为您从hash_map.get()或类似的地方获得了它,那么您可以跳过此步骤。
  • map(RefCell::borrow)用于调用.borrow()该值(如果存在)。这将创建一个Option<Ref<'_, _>>.
  • as_deref()相当于调用.deref()该值(如果存在)。

重要的是,以这种方式执行此操作,而不是尝试在单个调用中合并.borrow()和,因为这样可以保持中间值处于活动状态。.deref().map()Ref<'_, _>

a.as_ref().map(|a| a.borrow().deref())
Run Code Online (Sandbox Code Playgroud)
a.as_ref().map(RefCell::borrow).as_deref()
Run Code Online (Sandbox Code Playgroud)

另外,如果有多个这样的参数,并且您想将它们拆分为变量,请务必单独Ref<'_, _>使用该部分并在您使用它的地方使用它。同样,这也是保持中间体存活所必需的:.as_deref()Ref<'_, _>

let a_ref = a.as_ref().map(RefCell::borrow);
let b_ref = b.as_ref().map(RefCell::borrow);
let c_ref = c.as_ref().map(RefCell::borrow);

f(a_ref.as_deref(), b_ref.as_deref(), c_ref.as_deref());
Run Code Online (Sandbox Code Playgroud)