[Day38] Read Rust Atomics and Locks - Build Basic Arc

by Mara Bos

At Topic: Chapter 6. Basic Reference Counting

Recall

Arc

  • Goal: To share ownership
  • Thread safe (While Rc is not)
  • Immutable (So is Rc)
let a = Arc::new([1, 2, 3]); let b = a.clone(); assert_eq!(a.as_ptr(), b.as_ptr()); // Same allocation!

Notes

To see full implementation, please go here. I just write down things that I feel important.

ArcData<T> is the core unit of Arc and should not be public

struct ArcData<T> { ref_count: AtomicUsize, data: T }

Arc is basically a pointer to a shared ArcData<T> object. For this:

  • Box<ArcData<T>> is unacceptable because a Box represents exclusive ownership, not shared ownership.
  • Using reference is also unacceptable because of the lifetime issue (Arc does not follow the lifetime rule).
  • Instead, std::ptr::NonNull<T> is used which represents a pointer to T that is never null.
pub struct Arc<T> { ptr: NonNull<ArcData<T>> }

For

pub fn new(data: T) -> Arc<T> { Arc { ptr: NonNull::from(Box::leak(Box::new(ArcData { ref_count: AtomicUsize::new(1), data, }))), } }
  • Using Box::new() to allocate memory not &ArcData because &ArcData causes lifetime issues.

  • Box::leak() is used to give up the exclusive ownership of the allocation.

  • Box::from_raw(): reclaim exclusive ownership of the allocation (for dropping)

  • An Arc should not implement DerefMut since it represents shared ownership.

  • When ref_count == 1, get mutable reference is possible.

  • After changing the value by get_mut, we need to use mutex etc. to access the value of Arc (Actually, I do not understand why it is necessary).

References