您什么时候需要使用Cell或RefCell?似乎有许多其他类型选择适合代替这些,文档警告说使用RefCell是一种"最后的手段".
使用这些类型是" 代码味 "吗?任何人都可以展示一个例子,使用这些类型比使用其他类型更有意义,例如Rc甚至Box?
我有一个内部可变性的结构.
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
--> …Run Code Online (Sandbox Code Playgroud) encapsulation contravariance mutability rust interior-mutability
Rust 文档涵盖的范围Rc<RefCell<T>>非常广泛,但没有RefCell<Rc<T>>涉及我现在遇到的 。
这些是否有效地给出了相同的结果?它们之间有重要区别吗?
我有一个RefCell<HashMap>并想借用表,找到一个键,并返回对结果的引用:
use std::cell::RefCell;
use std::collections::HashMap;
struct Frame {
map: RefCell<HashMap<String, String>>,
}
impl Frame {
fn new() -> Frame {
Frame {
map: RefCell::new(HashMap::new()),
}
}
fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> {
self.map.borrow().get(k)
}
}
fn main() {
let f = Frame::new();
println!("{}", f.lookup(&"hello".to_string()).expect("blargh!"));
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
如果我删除RefCell然后一切正常:
struct Frame {
map: HashMap<String, String>,
}
impl Frame {
fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> {
self.map.get(k)
}
}
Run Code Online (Sandbox Code Playgroud)
在不复制哈希表中的字符串的情况下编写查找函数的正确方法是什么?
标准单元结构提供内部可变性,但只允许少数变异方法,如 set()、swap() 和 replace()。所有这些方法都会改变 Cell 的整个内容。但是,有时需要更具体的操作,例如,仅更改 Cell 中包含的部分数据。
所以我尝试实现某种通用 Cell,允许任意数据操作。操作由用户定义的闭包表示,该闭包接受单个参数 - &mut 对 Cell 内部数据的引用,因此用户自己可以决定如何处理 Cell 内部。下面的代码演示了这个想法:
use std::cell::UnsafeCell;
struct MtCell<Data>{
dcell: UnsafeCell<Data>,
}
impl<Data> MtCell<Data>{
fn new(d: Data) -> MtCell<Data> {
return MtCell{dcell: UnsafeCell::new(d)};
}
fn exec<F, RetType>(&self, func: F) -> RetType where
RetType: Copy,
F: Fn(&mut Data) -> RetType
{
let p = self.dcell.get();
let pd: &mut Data;
unsafe{ pd = &mut *p; }
return func(pd);
}
}
// test:
type MyCell = MtCell<usize>;
fn main(){ …Run Code Online (Sandbox Code Playgroud) use std::rc::Rc;
use std::cell::RefCell;
// Don't want to copy for performance reasons
struct LibraryData {
// Fields ...
}
// Creates and mutates data field in methods
struct LibraryStruct {
// Only LibraryStruct should have mutable access to this
data: Rc<RefCell<LibraryData>>
}
impl LibraryStruct {
pub fn data(&self) -> Rc<RefCell<LibraryData>> {
self.data.clone()
}
}
// Receives data field from LibraryStruct.data()
struct A {
data: Rc<RefCell<LibraryData>>
}
impl A {
pub fn do_something(&self) {
// Do something with self.data …Run Code Online (Sandbox Code Playgroud) 我的 C++ 背景让我对内部可变性感到不舒服。下面的代码是我围绕这个主题的调查。
我同意,从借用检查器的角度来看,处理每个内部状态可能迟早改变的单个结构上的许多引用是不可能的;这显然是内部可变性可以提供帮助的地方。
此外,在第15.5章“RefCell 和内部可变性模式”中锈病编程语言,有关的例子Messenger特质及其对实施
MockMessenger结构让我觉得,这是一种常见的API设计,系统地喜欢&self过&mut self,即使它很明显,迟早会强制要求某种可变性。Messenger发送消息时如何实现不改变其内部状态?例外只是打印消息,这与 一致&self,但一般情况可能包括写入某种内部流,这可能意味着缓冲,更新错误标志......所有这些当然都需要&mut self,例如
impl Write for File.
依靠内部的可变性来解决这个问题听起来好像是,在C ++中,const_cast荷兰国际集团或滥用mutable成员只是因为在申请的其他地方,我们是不是一致
const岬(对于C ++的学习者常犯的错误)。
所以,回到我下面的示例代码,我应该:
&mut self(编译器不会抱怨,即使它不是强制性的)从change_e()到change_i()为了与我改变存储的整数值的事实保持一致?&self,因为内部可变性允许它,即使我实际上改变了存储的整数的值?这个决定不仅对结构本身来说是局部的,而且会对使用这个结构的应用程序中可以表达的内容产生很大的影响。第二种解决方案肯定会有很大帮助,因为只涉及共享引用,但它是否与 Rust 中的预期一致。
我在Rust API Guidelines 中找不到这个问题的答案 。是否有任何其他类似于C++CoreGuidelines 的Rust 文档 ?
/*
$ rustc int_mut.rs && ./int_mut …Run Code Online (Sandbox Code Playgroud) 我有一个struct,称之为Book,比方说存储书店出售的一本书的数据。它需要在某些数据结构中的许多地方被引用(例如 with Rc),因此不能以正常方式可变地借用。然而,它有一些属性,比如它的价格,需要在初始化之后的某个时间填写,在对象已经有未完成的引用之后。
到目前为止,我可以想到两种方法来做到这一点,但它们都有缺点:
内部可变性:给出Book一个字段,例如 price: RefCell<Option<i32>>which 被初始化为RefCell::new(Option::None)whenBook被初始化。稍后,当我们确定书的价格时,我们可以使用borrow_mutto set pricetoSome(10)代替,然后我们可以使用borrow它来检索它的价值。
我的感觉是,一般来说,除非有必要,否则人们希望避免内部可变性,而且这里似乎没有那么必要。这种技术也有点尴尬,因为Option我们需要它,因为价格直到稍后才会有值(并且将其设置为0或-1同时看起来不像 Rustlike),但是它需要很多地方matches 或unwraps我们可以从逻辑上确定价格已经被填写。
单独的表:根本不将价格存储在里面Book,而是创建一个单独的数据结构来存储它,例如price_table: HashMap<Rc<Book>, i32>. 有一个函数,它在价格确定时创建并填充该表,然后通过引用(可变或不可变)将其传递给需要知道或更改书籍价格的每个函数。
像我一样来自 C 背景,HashMap感觉在速度和内存方面都是不必要的开销,因为数据已经有一个自然的地方(内部Book)并且“应该”通过一个简单的指针追逐来访问。这个解决方案还意味着我必须用一个引用price_table.
这两种方法中的一种在 Rust 中通常更惯用,还是有其他方法可以避免这种困境?我确实看到了Once,但我认为这不是我想要的,因为我仍然需要在初始化时知道如何填写price,而我不知道。
当然,在其他应用程序中,我们可能需要一些其他类型而不是i32表示我们想要的属性,所以我希望能够处理一般情况。
如果我猜对了std::rc::Rc,就不可能在 Rust 中通过a创建可变借用,您必须使用Cell或RefCell。但无论如何我无法理解如何使用它们。例如考虑这个简单的例子:
use std::cell::RefCell;
struct X (i32);
impl X {
fn foo(&mut self) {
self.0 = 0;
}
}
fn main () {
let x = X(5);
let rcx = RefCell::new(&x);
let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();
(*mutx).foo();
}
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
16:5: 16:9 error: cannot borrow immutable local variable `mutx` as mutable
16 mutx.foo();
Run Code Online (Sandbox Code Playgroud)
但是,如果我从行中删除引用(并更新类型mutx):
let rcx = RefCell::new(x);
Run Code Online (Sandbox Code Playgroud)
一切安好。但我不明白为什么,因为RefMut::deref_mut() -> &mut T在第 16 …
我试图创建一个方法,它返回了值的迭代器HashMap是内部的盒装RefCell,但我在那里的错误Ref被退回RefCell::borrow 不足够长寿的迭代器从该方法返回。这是我的代码:
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Values;
struct Foo {
map: Rc<RefCell<HashMap<i32, i32>>>,
}
impl Foo {
fn iter(&self) -> Values<i32, i32> {
self.map.borrow().values()
}
}
fn main() {
let foo = Foo {
map: Rc::new(RefCell::new(HashMap::new()))
};
for v in foo.iter() {
println!("{}", v)
}
}
Run Code Online (Sandbox Code Playgroud)
编译错误:
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Values;
struct Foo {
map: Rc<RefCell<HashMap<i32, i32>>>,
}
impl Foo {
fn iter(&self) -> Values<i32, i32> …Run Code Online (Sandbox Code Playgroud)