这几乎总是不安全的。
AnArc<T>只是一个指向堆分配结构的指针,大致看起来像
struct ArcInner<T: ?Sized> {
strong: atomic::AtomicUsize,
weak: atomic::AtomicUsize,
data: T, // You get a raw pointer to this element
}
into_raw()给你一个指向data元素的指针。的实现Arc::from_raw()采用这样一个指针,假设它是指向dataan 中的 -element的指针ArcInner<T>,返回内存并假设在那里找到 an ArcInner<T>。这个假设取决于 的内存布局T,特别是它的对齐方式,因此它在ArcInner.
如果您调用into_raw()anArc<U>然后调用from_raw(),就好像它是Arc<V>whereU并且对齐方式不同,则 where / is inV的偏移量计算将是错误的,并且调用 to将破坏数据结构。因此不需要取消引用来触发内存不安全。UVArcInner.clone()T
在实践中,这可能不是问题:由于data是两个元素之后的第三个usize元素,因此大多数元素T可能会以相同的方式对齐。但是,如果 stdlib 实现发生变化,或者您最终为这个假设错误的平台进行编译,那么重建由内存布局不同Arc<V>::from_raw的地方创建的平台将不安全并崩溃。Arc<U>VU
更新:
再想一想,我将我的投票从“可能是安全的,但令人毛骨悚然”降级为“很可能不安全”,因为我总能做到
#[repr(align(32))]
struct Foo;
let foo = Arc::new(Foo);
在此示例Foo中,将对齐为 32 个ArcInner<Foo>字节,大小为 32 个字节(8+8+16+0),而 aArcInner<()>仅为 16 个字节(8+8+0+0)。由于T在擦除类型后无法判断对齐方式是什么,因此无法重建有效的Arc.
有一个在实践中可能是安全的逃生舱口:通过包裹T到另一个Box,布局ArcInner<T>总是相同的。为了对任何用户强制执行此操作,您可以执行类似的操作
struct ArcBox<T>(Arc<Box<T>>)
并Deref在此实施。使用ArcBox而不是Arc强制内存布局ArcInner始终相同,因为T在另一个指针后面。然而,这意味着所有访问都T需要双重取消引用,这可能会严重影响性能。