mirror of https://github.com/rust-lang/nomicon
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
74 lines
2.6 KiB
74 lines
2.6 KiB
# Layout
|
|
|
|
Let's start by making the layout for our implementation of `Arc`.
|
|
|
|
An `Arc<T>` provides thread-safe shared ownership of a value of type `T`,
|
|
allocated in the heap. Sharing implies immutability in Rust, so we don't need to
|
|
design anything that manages access to that value, right? Although interior
|
|
mutability types like Mutex allow Arc's users to create shared mutability, Arc
|
|
itself doesn't need to concern itself with these issues.
|
|
|
|
However there _is_ one place where Arc needs to concern itself with mutation:
|
|
destruction. When all the owners of the Arc go away, we need to be able to
|
|
`drop` its contents and free its allocation. So we need a way for an owner to
|
|
know if it's the _last_ owner, and the simplest way to do that is with a count
|
|
of the owners -- Reference Counting.
|
|
|
|
Unfortunately, this reference count is inherently shared mutable state, so Arc
|
|
_does_ need to think about synchronization. We _could_ use a Mutex for this, but
|
|
that's overkill. Instead, we'll use atomics. And since everyone already needs a
|
|
pointer to the T's allocation, we might as well put the reference count in that
|
|
same allocation.
|
|
|
|
Naively, it would look something like this:
|
|
|
|
```rust
|
|
use std::sync::atomic;
|
|
|
|
pub struct Arc<T> {
|
|
ptr: *mut ArcInner<T>,
|
|
}
|
|
|
|
pub struct ArcInner<T> {
|
|
rc: atomic::AtomicUsize,
|
|
data: T,
|
|
}
|
|
```
|
|
|
|
This would compile, however it would be incorrect. First of all, the compiler
|
|
will give us too strict variance. For example, an `Arc<&'static str>` couldn't
|
|
be used where an `Arc<&'a str>` was expected. More importantly, it will give
|
|
incorrect ownership information to the drop checker, as it will assume we don't
|
|
own any values of type `T`. As this is a structure providing shared ownership of
|
|
a value, at some point there will be an instance of this structure that entirely
|
|
owns its data. See [the chapter on ownership and lifetimes](../ownership.md) for
|
|
all the details on variance and drop check.
|
|
|
|
To fix the first problem, we can use `NonNull<T>`. Note that `NonNull<T>` is a
|
|
wrapper around a raw pointer that declares that:
|
|
|
|
* We are covariant over `T`
|
|
* Our pointer is never null
|
|
|
|
To fix the second problem, we can include a `PhantomData` marker containing an
|
|
`ArcInner<T>`. This will tell the drop checker that we have some notion of
|
|
ownership of a value of `ArcInner<T>` (which itself contains some `T`).
|
|
|
|
With these changes we get our final structure:
|
|
|
|
```rust
|
|
use std::marker::PhantomData;
|
|
use std::ptr::NonNull;
|
|
use std::sync::atomic::AtomicUsize;
|
|
|
|
pub struct Arc<T> {
|
|
ptr: NonNull<ArcInner<T>>,
|
|
phantom: PhantomData<ArcInner<T>>,
|
|
}
|
|
|
|
pub struct ArcInner<T> {
|
|
rc: AtomicUsize,
|
|
data: T,
|
|
}
|
|
```
|