Rust,std::cell::Cell - 获取对内部数据的不可变引用
Rust, std::cell::Cell - get immutable reference to inner data
查看 std::cell::Cell 的文档,我在任何地方都看不到如何检索对内部数据的非可变引用。只有get_mut
方法:https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut
我不想使用这个函数,因为我想要 &self
而不是 &self mut
。
我找到了另一种采用原始指针的解决方案:
use std::cell::Cell;
struct DbObject {
key: Cell<String>,
data: String
}
impl DbObject {
pub fn new(data: String) -> Self {
Self {
key: Cell::new("some_uuid".into()),
data,
}
}
pub fn assert_key(&self) -> &str {
// setup key in the future if is empty...
let key = self.key.as_ptr();
unsafe {
let inner = key.as_ref().unwrap();
return inner;
}
}
}
fn main() {
let obj = DbObject::new("some data...".into());
let key = obj.assert_key();
println!("Key: {}", key);
}
有没有不使用不安全的方法来做到这一点?如果没有,也许 RefCell 在这里会更实用?
感谢您的帮助!
首先,如果你有一个 &mut T
,你可以很容易地从中得到一个 &T
。所以你可以使用 get_mut
得到 &T
.
但是要从 Cell<T>
中获取 &mut T
,您需要该单元格是可变的,因为 get_mut
需要一个 &mut self
参数。这是通过设计获得对单元格内部对象的引用的唯一方法。
通过要求使用 &mut self
方法从单元格中获取引用,您可以在编译时使用借用检查器检查独占访问。请记住,一个单元格启用内部可变性,并且有一个方法 set(&self, val: T)
,即可以修改非 mut
绑定的值的方法!如果有 get(&self) -> &T
方法,借用检查器无法确保您在设置对象时不持有对内部对象的引用,这是不安全的。
TL;DR:按照设计,您无法从非 mut
Cell<T>
中得到 &T
。使用 get_mut
(需要 mut
单元格)或 set
/replace
(适用于非 mut
单元格)。如果这是不可接受的,请考虑使用 RefCell
,它可以让你从非 mut 实例中获得 &T
,但需要一些运行时成本。
除了@mcarton的回答,为了保持内部可变性的合理性,即不允许可变引用与其他引用共存,我们有三种不同的方式:
- 使用
unsafe
可能会出现未定义的行为。这就是 UnsafeCell
所做的。
- 进行一些运行时检查,涉及运行时开销。这是
RefCell
、RwLock
和 Mutex
使用的方法。
- 限制抽象可以完成的操作。这就是使用它的
Cell
、Atomic*
和(不稳定的)OnceCell
(and thus Lazy
所做的(注意线程安全类型也有运行时开销,因为它们需要提供某种锁定).每个都提供一组不同的允许操作:
因此,当您需要能够引用内容时,您不能选择 Cell
,因为它不是为此而设计的 - 显而易见的选择是 RefCell
,的确。
查看 std::cell::Cell 的文档,我在任何地方都看不到如何检索对内部数据的非可变引用。只有get_mut
方法:https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut
我不想使用这个函数,因为我想要 &self
而不是 &self mut
。
我找到了另一种采用原始指针的解决方案:
use std::cell::Cell;
struct DbObject {
key: Cell<String>,
data: String
}
impl DbObject {
pub fn new(data: String) -> Self {
Self {
key: Cell::new("some_uuid".into()),
data,
}
}
pub fn assert_key(&self) -> &str {
// setup key in the future if is empty...
let key = self.key.as_ptr();
unsafe {
let inner = key.as_ref().unwrap();
return inner;
}
}
}
fn main() {
let obj = DbObject::new("some data...".into());
let key = obj.assert_key();
println!("Key: {}", key);
}
有没有不使用不安全的方法来做到这一点?如果没有,也许 RefCell 在这里会更实用?
感谢您的帮助!
首先,如果你有一个 &mut T
,你可以很容易地从中得到一个 &T
。所以你可以使用 get_mut
得到 &T
.
但是要从 Cell<T>
中获取 &mut T
,您需要该单元格是可变的,因为 get_mut
需要一个 &mut self
参数。这是通过设计获得对单元格内部对象的引用的唯一方法。
通过要求使用 &mut self
方法从单元格中获取引用,您可以在编译时使用借用检查器检查独占访问。请记住,一个单元格启用内部可变性,并且有一个方法 set(&self, val: T)
,即可以修改非 mut
绑定的值的方法!如果有 get(&self) -> &T
方法,借用检查器无法确保您在设置对象时不持有对内部对象的引用,这是不安全的。
TL;DR:按照设计,您无法从非 mut
Cell<T>
中得到 &T
。使用 get_mut
(需要 mut
单元格)或 set
/replace
(适用于非 mut
单元格)。如果这是不可接受的,请考虑使用 RefCell
,它可以让你从非 mut 实例中获得 &T
,但需要一些运行时成本。
除了@mcarton的回答,为了保持内部可变性的合理性,即不允许可变引用与其他引用共存,我们有三种不同的方式:
- 使用
unsafe
可能会出现未定义的行为。这就是UnsafeCell
所做的。 - 进行一些运行时检查,涉及运行时开销。这是
RefCell
、RwLock
和Mutex
使用的方法。 - 限制抽象可以完成的操作。这就是使用它的
Cell
、Atomic*
和(不稳定的)OnceCell
(and thusLazy
所做的(注意线程安全类型也有运行时开销,因为它们需要提供某种锁定).每个都提供一组不同的允许操作:
因此,当您需要能够引用内容时,您不能选择 Cell
,因为它不是为此而设计的 - 显而易见的选择是 RefCell
,的确。