基本代码

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

构建 Arc

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

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

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

Send 和 Sync

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

这是没问题的,因为:

  • 当且仅当你拥有唯一的 Arc 引用时,你才能获得其引用数据的可变引用(这仅发生在Drop中)
  • 我们使用原子操作进行共享可变引用计数
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的实现。

我们需要导入该 Trait:

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

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

代码

下面是本节的所有代码。

use std::ops::Deref;

impl<T> Arc<T> {
    pub fn new(data: T) -> Arc<T> {
        // 当前的指针就是第一个引用,因此初始时设置 count 为 1
        let boxed = Box::new(ArcInner {
            rc: AtomicUsize::new(1),
            data,
        });
        Arc {
            // 我们从 Box::into_raw 得到该指针,因此使用 `.unwrap()` 是完全可行的
            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
    }
}