如何在不破坏封装的情况下 return 引用 RefCell 中的内容?
How do I return a reference to something inside a RefCell without breaking encapsulation?
我有一个具有内部可变性的结构。
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
上有一个returns借来的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.
现在我可以编写这样的函数 returns 整个内部:
pub fn get_mutable_interior(&self) -> std::cell::Ref<MutableInterior>;
然而,这可能会向 Foo
.
暴露真正私有实现细节的字段(本例中的 MutableInterior.hide_me
)
理想情况下,我只是想公开 vec
本身,可能有一个守卫来实现动态借用行为。那么来电者就不必去了解hide_me
.
您可以创建一个类似于 RefCell::borrow()
编写的 Ref<'a,T>
守卫 return 的新结构,以包装此 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()
方法更新为 return 一个 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;
}
您可以使用 Ref::map
(since Rust 1.8). This has the same result as :
而不是创建全新的类型
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)
}
}
您可以将 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
本身不提供内部可变性)。
我有一个具有内部可变性的结构。
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
上有一个returns借来的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 forRefCell<T>
s are tracked 'at runtime', unlike Rust's native reference types which are entirely tracked statically, at compile time. BecauseRefCell<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.
现在我可以编写这样的函数 returns 整个内部:
pub fn get_mutable_interior(&self) -> std::cell::Ref<MutableInterior>;
然而,这可能会向 Foo
.
MutableInterior.hide_me
)
理想情况下,我只是想公开 vec
本身,可能有一个守卫来实现动态借用行为。那么来电者就不必去了解hide_me
.
您可以创建一个类似于 RefCell::borrow()
编写的 Ref<'a,T>
守卫 return 的新结构,以包装此 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()
方法更新为 return 一个 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;
}
您可以使用 Ref::map
(since Rust 1.8). This has the same result as
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)
}
}
您可以将 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
本身不提供内部可变性)。