基本代码

现在我们已经确定了实现Arc的布局,让我们开始写一些基本代码。

构建 Arc

我们首先需要一种方法来构造一个Arc<T>

这很简单,因为我们只需要把ArcInner<T>扔到一个 Box 里并得到一个NonNull<T>的指针。

  1. impl<T> Arc<T> {
  2. pub fn new(data: T) -> Arc<T> {
  3. // 当前的指针就是第一个引用,因此初始时设置 count 为 1
  4. let boxed = Box::new(ArcInner {
  5. rc: AtomicUsize::new(1),
  6. data,
  7. });
  8. Arc {
  9. // 我们从 Box::into_raw 得到该指针,因此使用 `.unwrap()` 是完全可行的
  10. ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
  11. phantom: PhantomData,
  12. }
  13. }
  14. }

Send 和 Sync

由于我们正在构建并发原语,因此我们需要能够跨线程发送它。因此,我们可以实现SendSync标记特性。有关这些的更多信息,请参阅有关SendSync的部分

这是没问题的,因为:

  • 当且仅当你拥有唯一的 Arc 引用时,你才能获得其引用数据的可变引用(这仅发生在Drop中)
  • 我们使用原子操作进行共享可变引用计数
  1. unsafe impl<T: Sync + Send> Send for Arc<T> {}
  2. 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函数不同,所以我们必须这样调用它。

  1. unsafe { self.ptr.as_ref() }

在这段代码中,我们将多次使用这个片段(通常与相关的let绑定)。

这种不安全是没问题的,因为当这个Arc存活的时候,我们可以保证内部指针是有效的。

Deref

好了。现在我们可以制作Arc了(很快就能正确地克隆和销毁它们),但是我们怎样才能获得里面的数据呢?

我们现在需要的是一个Deref的实现。

我们需要导入该 Trait:

  1. use std::ops::Deref;

这里是实现:

  1. impl<T> Deref for Arc<T> {
  2. type Target = T;
  3. fn deref(&self) -> &T {
  4. let inner = unsafe { self.ptr.as_ref() };
  5. &inner.data
  6. }
  7. }

看着很简单,对不?这只是解除了对ArcInner<T>NonNull指针的引用,然后得到了对里面数据的引用。

代码

下面是本节的所有代码。

  1. use std::ops::Deref;
  2. impl<T> Arc<T> {
  3. pub fn new(data: T) -> Arc<T> {
  4. // 当前的指针就是第一个引用,因此初始时设置 count 为 1
  5. let boxed = Box::new(ArcInner {
  6. rc: AtomicUsize::new(1),
  7. data,
  8. });
  9. Arc {
  10. // 我们从 Box::into_raw 得到该指针,因此使用 `.unwrap()` 是完全可行的
  11. ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
  12. phantom: PhantomData,
  13. }
  14. }
  15. }
  16. unsafe impl<T: Sync + Send> Send for Arc<T> {}
  17. unsafe impl<T: Sync + Send> Sync for Arc<T> {}
  18. impl<T> Deref for Arc<T> {
  19. type Target = T;
  20. fn deref(&self) -> &T {
  21. let inner = unsafe { self.ptr.as_ref() };
  22. &inner.data
  23. }
  24. }