Rust 中的 `Cell` 可以安全地用于 `Rc` 吗?
Could `Cell` in Rust be safely used on `Rc` specifically?
已经确定(例如在 中)Rust 的 Cell
不适用于非 Copy
类型,因为 .get()
操作只有在可以保证 cloning/copying 从 Cell
出来的数据不会被分配给 Cell
.
的东西打断
因此,Rust 中的当前限制是 Cell.get()
仅在单元格内的类型为 Copy
时可用; Clone
是不够的,因为不能保证 .clone()
调用不会以某种方式设法访问正在克隆的 Cell
并在它处于中间时写入它被阅读。所以像这样的代码是非法的,因为编译器不确定 CloneableObject.clone()
不会做一些荒谬的事情:
use std::cell::Cell;
#[derive(Debug, Clone)]
struct CloneableObject {}
fn main() {
let x = Cell::new(CloneableObject {});
println!("{:?}", x.get());
}
我的问题是关于 Cell
包装的类型是标准库类型 Rc
的特殊情况。 Rc
的特殊之处在于 Rc.clone()
实际上不进行任何复制,也不检查 Rc
指向的值;它只是在 Rc
中递增一个整数,这不可能写入包含 Rc
.
的 Cell
据推测,这意味着像下面这样的代码可以安全地合法:
use std::cell::Cell;
use std::rc::Rc;
fn main() {
let x = Cell::new(Rc::new(0i32));
println!("{:?}", x.get());
}
但是目前编译不了,因为Cell
和Rc
没有特例。
我的问题是:是否有任何 safety/soundness 理由不为 Cell<Rc<T>>
添加这种特殊情况?或者这仅仅是“这将是完全安全的,但我们还没有教编译器如何识别它是安全的”的情况? (如果这样做 会 是安全的,我可以将其作为功能建议提交——由于不需要运行时检查,它会比 RefCell<Rc<T>>
更有效——但我想确保我没有遗漏任何不健全的地方。)
为了避免混淆:我在这个问题中故意问的是Cell<Rc<T>>
,而不是更常用的Rc<Cell<T>>
/Rc<RefCell<T>>
.
是的,它可能可以,但在目前的状态下,Rust 缺乏表明这一点所需的专业化功能。
理论上,可能有一个特征表明可以安全地从 Cell::get
:
克隆一个类型
use std::cell::UnsafeCell;
use std::rc::Rc;
pub struct Cell<T> {
inner: UnsafeCell<T>,
}
impl<T> Cell<T> {
pub fn set(&self, value: T) {
unsafe { *self.inner.get() = value };
}
pub fn get(&self) -> T
where
T: CellCloneable,
{
unsafe { *self.inner.get() }.clone()
}
}
trait CellCloneable: Clone {}
impl<T> CellCloneable for T where T: Copy {}
impl<T> CellCloneable for Rc<T> {}
不幸的是,由于缺乏专业化功能,这也无法编译。
并且由于编译器无法保证实现者不会最终调用 Cell::set
自身,因此它必须是 unsafe
特征。这个特性很容易出错:任何动态调度都会使这个安全约束无法保证,并且不相关的代码可以更新以违反这个约束而不使用任何 unsafe
本身。
IMO,这一添加将违背 Cell
的目标,即成为内部可变性的相当基本和安全的原语。
已经确定(例如在 Cell
不适用于非 Copy
类型,因为 .get()
操作只有在可以保证 cloning/copying 从 Cell
出来的数据不会被分配给 Cell
.
因此,Rust 中的当前限制是 Cell.get()
仅在单元格内的类型为 Copy
时可用; Clone
是不够的,因为不能保证 .clone()
调用不会以某种方式设法访问正在克隆的 Cell
并在它处于中间时写入它被阅读。所以像这样的代码是非法的,因为编译器不确定 CloneableObject.clone()
不会做一些荒谬的事情:
use std::cell::Cell;
#[derive(Debug, Clone)]
struct CloneableObject {}
fn main() {
let x = Cell::new(CloneableObject {});
println!("{:?}", x.get());
}
我的问题是关于 Cell
包装的类型是标准库类型 Rc
的特殊情况。 Rc
的特殊之处在于 Rc.clone()
实际上不进行任何复制,也不检查 Rc
指向的值;它只是在 Rc
中递增一个整数,这不可能写入包含 Rc
.
Cell
据推测,这意味着像下面这样的代码可以安全地合法:
use std::cell::Cell;
use std::rc::Rc;
fn main() {
let x = Cell::new(Rc::new(0i32));
println!("{:?}", x.get());
}
但是目前编译不了,因为Cell
和Rc
没有特例。
我的问题是:是否有任何 safety/soundness 理由不为 Cell<Rc<T>>
添加这种特殊情况?或者这仅仅是“这将是完全安全的,但我们还没有教编译器如何识别它是安全的”的情况? (如果这样做 会 是安全的,我可以将其作为功能建议提交——由于不需要运行时检查,它会比 RefCell<Rc<T>>
更有效——但我想确保我没有遗漏任何不健全的地方。)
为了避免混淆:我在这个问题中故意问的是Cell<Rc<T>>
,而不是更常用的Rc<Cell<T>>
/Rc<RefCell<T>>
.
是的,它可能可以,但在目前的状态下,Rust 缺乏表明这一点所需的专业化功能。
理论上,可能有一个特征表明可以安全地从 Cell::get
:
use std::cell::UnsafeCell;
use std::rc::Rc;
pub struct Cell<T> {
inner: UnsafeCell<T>,
}
impl<T> Cell<T> {
pub fn set(&self, value: T) {
unsafe { *self.inner.get() = value };
}
pub fn get(&self) -> T
where
T: CellCloneable,
{
unsafe { *self.inner.get() }.clone()
}
}
trait CellCloneable: Clone {}
impl<T> CellCloneable for T where T: Copy {}
impl<T> CellCloneable for Rc<T> {}
不幸的是,由于缺乏专业化功能,这也无法编译。
并且由于编译器无法保证实现者不会最终调用 Cell::set
自身,因此它必须是 unsafe
特征。这个特性很容易出错:任何动态调度都会使这个安全约束无法保证,并且不相关的代码可以更新以违反这个约束而不使用任何 unsafe
本身。
IMO,这一添加将违背 Cell
的目标,即成为内部可变性的相当基本和安全的原语。