Dre*_*rew 21 encapsulation contravariance mutability rust 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();
}
Run Code Online (Sandbox Code Playgroud)
产生错误:
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 | | }
| |_____^
Run Code Online (Sandbox Code Playgroud)
问题是我不能有一个Foo返回借用的函数vec,因为借用vec只在生命周期内有效Ref,但是它Ref会立即超出范围.
我认为Ref必须坚持下去,因为:
RefCell<T>使用Rust的生命周期来实现"动态借用",这是一个可以声称对内在值进行临时,独占,可变访问的过程.对于RefCell<T>s的借位是"在运行时"跟踪的,不同于Rust的本机引用类型,它们在编译时被完全静态跟踪.因为RefCell<T>借用是动态的,所以可以尝试借用已经可以借来的价值; 当发生这种情况时,会导致任务恐慌.
现在我可以编写一个这样的函数来返回整个内部:
pub fn get_mutable_interior(&self) -> std::cell::Ref<MutableInterior>;
Run Code Online (Sandbox Code Playgroud)
但是,这可能会暴露MutableInterior.hide_me实际私有实现细节的字段(在此示例中)Foo.
理想情况下,我只想暴露vec自己,可能会有一个守卫来实现动态借用行为.然后呼叫者不必了解hide_me.
Lev*_*ans 19
你可以创建一个类似于Ref<'a,T>返回的守护的新结构RefCell::borrow(),以包装它Ref并避免它超出范围,如下所示:
use std::cell::Ref;
struct FooGuard<'a> {
guard: Ref<'a, MutableInterior>,
}
Run Code Online (Sandbox Code Playgroud)
然后,你可以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
}
}
Run Code Online (Sandbox Code Playgroud)
之后,更新您的get_items()方法以返回FooGuard实例:
impl Foo {
pub fn get_items(&self) -> FooGuard {
FooGuard {
guard: self.interior.borrow(),
}
}
}
Run Code Online (Sandbox Code Playgroud)
并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;
}
Run Code Online (Sandbox Code Playgroud)
She*_*ter 12
您可以使用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)
}
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用新功能,例如从API中impl 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)
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将 包装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();
}
Run Code Online (Sandbox Code Playgroud)
当您需要改变 时Vec,请使用Rc::make_mut来获取对 的可变引用Vec。如果还有其他Rcs 引用Vec,make_mut则将sRc与其他Rcs 分离,克隆Vec并更新自身以引用那个 new Vec,然后给你一个可变引用。这确保了其他Rcs 中的值不会突然改变(因为Rc它本身不提供内部可变性)。
| 归档时间: |
|
| 查看次数: |
2487 次 |
| 最近记录: |