rust - 如何在不破坏封装的情况下返回对 RefCell 中某些内容的引用?

标签 rust encapsulation contravariance mutability interior-mutability

我有一个具有内部可变性的结构。

use std::cell::RefCell;

struct MutableInterior {
    hide_me: i32,
    vec: Vec<i32>,
}
struct Foo {
    //although not used in this particular snippet,
    //the motivating problem uses interior mutability
    //via RefCell.
    interior: RefCell<MutableInterior>,
}

impl Foo {
    pub fn get_items(&self) -> &Vec<i32> {
        &self.interior.borrow().vec
    }
}

fn main() {
    let f = Foo {
        interior: RefCell::new(MutableInterior {
            vec: Vec::new(),
            hide_me: 2,
        }),
    };
    let borrowed_f = &f;
    let items = borrowed_f.get_items();
}

产生错误:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:16:10
   |
16 |         &self.interior.borrow().vec
   |          ^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
17 |     }
   |     - temporary value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 15:5...
  --> src/main.rs:15:5
   |
15 | /     pub fn get_items(&self) -> &Vec<i32> {
16 | |         &self.interior.borrow().vec
17 | |     }
   | |_____^

问题是我无法在 Foo 上使用函数返回借来的 vec , 因为借了vec仅在 Ref 的生命周期内有效,但是 Ref立即超出范围。

我认为 Ref必须坚持 because :

RefCell<T> uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can claim temporary, exclusive, mutable access to the inner value. Borrows for RefCell<T>s are tracked 'at runtime', unlike Rust's native reference types which are entirely tracked statically, at compile time. Because RefCell<T> borrows are dynamic it is possible to attempt to borrow a value that is already mutably borrowed; when this happens it results in task panic.

现在我可以改为编写一个返回整个内部的函数:

pub fn get_mutable_interior(&self) -> std::cell::Ref<MutableInterior>;

然而,这可能会向 MutableInterior.hide_me 公开字段(在本例中为 Foo),这些字段是真正私有(private)的实现细节。 .

理想情况下,我只想公开 vec本身,可能有一个守卫来实现动态借用行为。这样调用者就不必了解 hide_me 了。 .

最佳答案

您可以使用 Ref::map 而不是创建一个全新的类型(自 Rust 1.8 起)。这与 Levans' existing answer 具有相同的结果:

use std::cell::Ref;

impl Foo {
    pub fn get_items(&self) -> Ref<'_, Vec<i32>> {
        Ref::map(self.interior.borrow(), |mi| &mi.vec)
    }
}

您还可以使用 impl Trait 等新功能从 API 中隐藏 Ref:

use std::cell::Ref;
use std::ops::Deref;

impl Foo {
    pub fn get_items(&self) -> impl Deref<Target = Vec<i32>> + '_ {
        Ref::map(self.interior.borrow(), |mi| &mi.vec)
    }
}

关于rust - 如何在不破坏封装的情况下返回对 RefCell 中某些内容的引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58838253/

相关文章:

hashmap - 以忽略 HashMap 值的通用方式公开 HashMap

C++ - 从另一个类构造函数调用类构造函数

Java:按钮封装

javascript - 我们需要将 ES6 代码包装在 IIFE 中吗?

generics - Kotlin 泛型类型参数

c# - 参数必须是输入安全错误

c# - 协变和逆变之间的区别

rust - 我如何通过询问 "can it be turned into a number"来移植识别值属于哪个 CSV header 的 Python 代码?

rust - 如何制作一个也跳过面向行的注释的 nom 空白解析器?

rust - 理解 rust Option as_mut 方法