基础代码
现在我们已经确定了 Arc
实现的布局,让我们创建一些基础代码。
构造 Arc
我们首先需要一种构造 Arc<T>
的方法。
这非常简单,因为我们只需要将 ArcInner<T>
装箱并获取指向它的 NonNull<T>
指针。
impl<T> Arc<T> {
pub fn new(data: T) -> Arc<T> {
// We start the reference count at 1, as that first reference is the
// current pointer.
let boxed = Box::new(ArcInner {
rc: AtomicUsize::new(1),
data,
});
Arc {
// It is okay to call `.unwrap()` here as we get a pointer from
// `Box::into_raw` which is guaranteed to not be null.
ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
phantom: PhantomData,
}
}
}
Send 和 Sync
由于我们正在构建一个并发原语,因此我们需要能够跨线程发送它。因此,我们可以实现 Send
和 Sync
标记特征。有关这些的更多信息,请参阅关于 Send
和 Sync
的部分。
这样做是可以的,因为
- 当且仅当它是引用该数据的唯一
Arc
时(这仅在Drop
中发生),您才能获得对Arc
内部值的 mutable 引用 - 我们对共享的可变引用计数使用原子操作
unsafe impl<T: Sync + Send> Send for Arc<T> {}
unsafe impl<T: Sync + Send> Sync for Arc<T> {}
我们需要有约束 T: Sync + Send
,因为如果我们不提供这些约束,则可以通过 Arc
在线程边界之间共享线程不安全的值,这可能会导致数据竞争或不健全。
例如,如果不存在这些约束,则 Arc<Rc<u32>>
将是 Sync
或 Send
,这意味着您可以从 Arc
中克隆 Rc
以将其发送到另一个线程(无需创建全新的 Rc
),这将创建数据竞争,因为 Rc
不是线程安全的。
获取 ArcInner
要将 NonNull<T>
指针解引用为 &T
,我们可以调用 NonNull::as_ref
。与典型的 as_ref
函数不同,这是不安全的,因此我们必须像这样调用它
unsafe { self.ptr.as_ref() }
我们将在本代码中多次使用此代码段(通常与关联的 let
绑定一起使用)。
这种不安全性是可以的,因为在此 Arc
存活期间,我们保证内部指针是有效的。
Deref
好的。现在我们可以创建 Arc
(并且很快将能够正确地克隆和销毁它们),但是我们如何获取内部的数据呢?
我们现在需要的是 Deref
的实现。
我们需要导入该特征
use std::ops::Deref;
这是实现
impl<T> Deref for Arc<T> {
type Target = T;
fn deref(&self) -> &T {
let inner = unsafe { self.ptr.as_ref() };
&inner.data
}
}
很简单,对吧?这只是将 NonNull
指针解引用到 ArcInner<T>
,然后获取对内部数据的引用。
代码
以下是本节中的所有代码
use std::ops::Deref;
impl<T> Arc<T> {
pub fn new(data: T) -> Arc<T> {
// We start the reference count at 1, as that first reference is the
// current pointer.
let boxed = Box::new(ArcInner {
rc: AtomicUsize::new(1),
data,
});
Arc {
// It is okay to call `.unwrap()` here as we get a pointer from
// `Box::into_raw` which is guaranteed to not be null.
ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
phantom: PhantomData,
}
}
}
unsafe impl<T: Sync + Send> Send for Arc<T> {}
unsafe impl<T: Sync + Send> Sync for Arc<T> {}
impl<T> Deref for Arc<T> {
type Target = T;
fn deref(&self) -> &T {
let inner = unsafe { self.ptr.as_ref() };
&inner.data
}
}