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>>)