42

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

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必须坚持下去,因为

RefCell<T>使用 Rust 的生命周期来实现“动态借用”,这是一个可以声明对内部值的临时、独占、可变访问的过程。s 的借用RefCell<T>是在“运行时”跟踪的,这与 Rust 的本地引用类型不同,后者在编译时完全静态跟踪。因为RefCell<T>借用是动态的,所以可以尝试借用一个已经可变借用的值;当这种情况发生时,它会导致任务恐慌。

现在我可以写一个这样的函数来返回整个内部:

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

MutableInterior.hide_me但是,这可能会将真正私有的实现细节的字段(在此示例中)暴露给Foo.

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

4

3 回答 3

31

您可以创建一个类似于Ref<'a,T>返回的守卫的新结构RefCell::borrow(),以包装它Ref并避免它超出范围,如下所示:

use std::cell::Ref;

struct FooGuard<'a> {
    guard: Ref<'a, MutableInterior>,
}

然后,您可以Deref为它实现特征,以便可以像使用它一样使用它&Vec<i32>

use std::ops::Deref;

impl<'b> Deref for FooGuard<'b> {
    type Target = Vec<i32>;

    fn deref(&self) -> &Vec<i32> {
        &self.guard.vec
    }
}

之后,更新您的get_items()方法以返回一个FooGuard实例:

impl Foo {
    pub fn get_items(&self) -> FooGuard {
        FooGuard {
            guard: self.interior.borrow(),
        }
    }
}

Deref施展魔法:

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();
    let v: &Vec<i32> = &items;
}
于 2015-04-01T22:19:32.400 回答
31

Ref::map您可以使用(从 Rust 1.8 开始),而不是创建一个全新的类型。这与Levans 的现有答案具有相同的结果:

use std::cell::Ref;

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

您还可以使用新功能,例如从 APIimpl Trait中隐藏: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)
    }
}
于 2018-07-14T17:49:33.420 回答
5

您可以VecRc.

use std::cell::RefCell;
use std::rc::Rc;

struct MutableInterior {
    hide_me: i32,
    vec: Rc<Vec<i32>>,
}
struct Foo {
    interior: RefCell<MutableInterior>,
}

impl Foo {
    pub fn get_items(&self) -> Rc<Vec<i32>> {
        self.interior.borrow().vec.clone() // clones the Rc, not the Vec
    }
}

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

当您需要改变 时Vec,使用Rc::make_mut来获取对 的可变引用Vec。如果还有其他Rcs 引用Vecmake_mut则将Rc与其他Rcs 分离,克隆Vec并更新自身以引用该 new Vec,然后为您提供对它的可变引用。这确保了其他Rcs 中的值不会突然改变(因为Rc它本身不提供内部可变性)。

于 2018-07-15T06:08:20.957 回答