Rust 通过原始指针克隆类型擦除弧是否安全?
我所处的情况是,我正在使用包装在中的数据,有时我最终使用来获取指向底层数据的原始指针。我的用例还要求类型擦除,因此原始指针通常被转换为Rust 通过原始指针克隆类型擦除弧是否安全?,rust,unsafe,type-erasure,raw-pointer,Rust,Unsafe,Type Erasure,Raw Pointer,我所处的情况是,我正在使用包装在中的数据,有时我最终使用来获取指向底层数据的原始指针。我的用例还要求类型擦除,因此原始指针通常被转换为*const c_void,然后在重新构建弧时转换回适当的具体类型 我遇到了这样一种情况:在不需要知道底层数据的具体类型的情况下,能够克隆Arc。据我所知,只要我从未实际取消引用数据,就可以安全地使用伪类型重新构建弧,仅用于调用克隆。例如,这应该是安全的: pub-unsafe-fn-clone\u-raw(句柄:*常数c\u-void)->*常数c\u-void
*const c_void
,然后在重新构建弧时转换回适当的具体类型
我遇到了这样一种情况:在不需要知道底层数据的具体类型的情况下,能够克隆Arc
。据我所知,只要我从未实际取消引用数据,就可以安全地使用伪类型重新构建弧
,仅用于调用克隆
。例如,这应该是安全的:
pub-unsafe-fn-clone\u-raw(句柄:*常数c\u-void)->*常数c\u-void{
让原始=弧::从原始(手柄);
让copy=original.clone();
mem::忘记(原文);
Arc::进入_原始(复制)
}
我遗漏了什么会让这变得不安全的东西吗?此外,我假设答案也适用于Rc
,但如果有任何差异,请告诉我 这几乎总是不安全的
Arc
只是一个指向堆分配结构的指针,该结构大致如下
struct ArcInner<T: ?Sized> {
strong: atomic::AtomicUsize,
weak: atomic::AtomicUsize,
data: T, // You get a raw pointer to this element
}
在本例中,Foo
将与32字节对齐,使ArcInner
的大小为32字节(8+8+16+0),而ArcInner
的大小仅为16字节(8+8+0+0)。由于在类型被擦除后无法判断T
的对齐方式,因此无法重建有效的弧
有一个逃生舱口在实践中可能是安全的:通过将T
包装到另一个Box
中,ArcInner
的布局总是相同的。为了强制任何用户执行此操作,您可以执行以下操作
struct ArcBox<T>(Arc<Box<T>>)
struct-ArcBox(Arc)
并在此基础上执行Deref
。使用ArcBox
而不是Arc
强制ArcInner
的内存布局始终相同,因为T
位于另一个指针后面。但是,这意味着对T
的所有访问都需要双重取消引用,这可能会严重影响性能。clone
增加引用计数,并且从原始
,进入原始
和mem::forget
不要触摸它,因此,调用clone\u raw
的效果只是将refcount增加1。我假设这只是一个虚假的例子(即,在你的真实代码中,你会用original
做一些事情,而不是mem::forget
)?(我认为这是安全的,因为“不太可能做坏事”,我只是想确保我没有忽略一些奇怪的事情)是的,你的例子很差,这是安全的,但不太可能做你想做的事。事实上,您想要什么?在我给出的示例中,我需要执行mem::forget(original)
,这样就不会在函数末尾删除它,从而减少ref计数。另一个选择是改为使用Arc::into_raw(original)
,如图所示。答案很好。我添加了T:?大小的绑定到您的ArcInner
复制品中,因为这确保了数据始终位于ArcInner
布局的末尾——如果没有它,或者repr(C)
,Rust将能够将数据放置在它想要的任何地方(这意味着所有的赌注都被取消了,即使是对于T
没有usize
那么严格的赌注)。谢谢你的全面回答!我怀疑这不会按我想要的方式工作,但你的回答清楚了原因。
struct ArcBox<T>(Arc<Box<T>>)