我有一个内部可变性的结构。
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>
使用Rust的生命周期来实现“动态借用”,这是一个可以声称对内在值进行临时,独占,可变访问的过程。RefCell<T>
的借位在运行时被跟踪,不像Rust的本机引用类型,它们在编译时被完全静态跟踪。因为RefCell<T>
借用是动态的,所以可以尝试借用已经可变借用的值;当发生这种情况时,会导致任务恐慌。
现在我可以编写一个这样的函数来返回整个内部:
pub fn get_mutable_interior(&self) -> std::cell::Ref<MutableInterior>;
但是,这可能会公开MutableInterior.hide_me
的私有实现细节字段(在此示例中为Foo
)。
理想情况下,我只想公开vec
本身,可能会有一个守卫来实现动态借用行为。然后,来电者无需了解hide_me
。
答案 0 :(得分:18)
您可以创建一个类似于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;
}
答案 1 :(得分:8)
您可以使用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)
}
}
答案 2 :(得分:3)
您可以将Vec
包裹在Rc
中。
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
的可变引用。如果还有其他Rc
个指向Vec
的引用,make_mut
会将Rc
与其他Rc
分离,则克隆Vec
并更新自身以引用该新Vec
,然后为您提供可变的引用。这样可以确保其他Rc
中的值不会突然改变(因为Rc
本身不提供内部可变性)。