Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alloc::rc::UniqueRc #111849

Merged
merged 1 commit into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 139 additions & 3 deletions library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,12 +258,12 @@ use core::iter;
use core::marker::{PhantomData, Unsize};
#[cfg(not(no_global_oom_handling))]
use core::mem::size_of_val;
use core::mem::{self, align_of_val_raw, forget};
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
use core::mem::{self, align_of_val_raw, forget, ManuallyDrop};
use core::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver};
use core::panic::{RefUnwindSafe, UnwindSafe};
#[cfg(not(no_global_oom_handling))]
use core::pin::Pin;
use core::ptr::{self, NonNull};
use core::ptr::{self, drop_in_place, NonNull};
#[cfg(not(no_global_oom_handling))]
use core::slice::from_raw_parts_mut;

Expand Down Expand Up @@ -2744,3 +2744,139 @@ fn data_offset_align(align: usize) -> usize {
let layout = Layout::new::<RcBox<()>>();
layout.size() + layout.padding_needed_for(align)
}

/// A uniquely owned `Rc`
///
/// This represents an `Rc` that is known to be uniquely owned -- that is, have exactly one strong
/// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong
/// references will fail unless the `UniqueRc` they point to has been converted into a regular `Rc`.
///
/// Because they are uniquely owned, the contents of a `UniqueRc` can be freely mutated. A common
/// use case is to have an object be mutable during its initialization phase but then have it become
/// immutable and converted to a normal `Rc`.
///
/// This can be used as a flexible way to create cyclic data structures, as in the example below.
///
/// ```
/// #![feature(unique_rc_arc)]
/// use std::rc::{Rc, Weak, UniqueRc};
///
/// struct Gadget {
/// #[allow(dead_code)]
/// me: Weak<Gadget>,
/// }
///
/// fn create_gadget() -> Option<Rc<Gadget>> {
/// let mut rc = UniqueRc::new(Gadget {
/// me: Weak::new(),
/// });
/// rc.me = UniqueRc::downgrade(&rc);
/// Some(UniqueRc::into_rc(rc))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does create_gadget return an Option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example evolved a bit from my first version of it. Originally I wanted to show that you could use this to initialize a circular data structure while the initialization phase is fallible, which is something that the existing new_cyclic function doesn't handle well. Unfortunately, there's no fallibility left in this example so the Option is probably more of a distraction.

/// }
///
/// create_gadget().unwrap();
/// ```
///
/// An advantage of using `UniqueRc` over [`Rc::new_cyclic`] to build cyclic data structures is that
/// [`Rc::new_cyclic`]'s `data_fn` parameter cannot be async or return a [`Result`]. As shown in the
/// previous example, `UniqueRc` allows for more flexibility in the construction of cyclic data,
/// including fallible or async constructors.
#[unstable(feature = "unique_rc_arc", issue = "112566")]
#[derive(Debug)]
pub struct UniqueRc<T> {
Mark-Simulacrum marked this conversation as resolved.
Show resolved Hide resolved
ptr: NonNull<RcBox<T>>,
eholk marked this conversation as resolved.
Show resolved Hide resolved
phantom: PhantomData<RcBox<T>>,
}

impl<T> UniqueRc<T> {
/// Creates a new `UniqueRc`
///
/// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading
/// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`].
/// After converting the `UniqueRc` into an [`Rc`], any weak references created beforehand will
/// point to the new [`Rc`].
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn new(value: T) -> Self {
Self {
ptr: Box::leak(Box::new(RcBox {
strong: Cell::new(0),
// keep one weak reference so if all the weak pointers that are created are dropped
// the UniqueRc still stays valid.
weak: Cell::new(1),
value,
}))
.into(),
phantom: PhantomData,
}
}

/// Creates a new weak reference to the `UniqueRc`
///
/// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted
/// to a [`Rc`] using [`UniqueRc::into_rc`].
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn downgrade(this: &Self) -> Weak<T> {
// SAFETY: This pointer was allocated at creation time and we guarantee that we only have
// one strong reference before converting to a regular Rc.
unsafe {
this.ptr.as_ref().inc_weak();
}
Weak { ptr: this.ptr }
}

/// Converts the `UniqueRc` into a regular [`Rc`]
///
/// This consumes the `UniqueRc` and returns a regular [`Rc`] that contains the `value` that
/// is passed to `into_rc`.
///
/// Any weak references created before this method is called can now be upgraded to strong
/// references.
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn into_rc(this: Self) -> Rc<T> {
let mut this = ManuallyDrop::new(this);
// SAFETY: This pointer was allocated at creation time so we know it is valid.
unsafe {
// Convert our weak reference into a strong reference
this.ptr.as_mut().strong.set(1);
Rc::from_inner(this.ptr)
}
}
}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
impl<T> Deref for UniqueRc<T> {
type Target = T;

fn deref(&self) -> &T {
// SAFETY: This pointer was allocated at creation time so we know it is valid.
unsafe { &self.ptr.as_ref().value }
}
}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
impl<T> DerefMut for UniqueRc<T> {
fn deref_mut(&mut self) -> &mut T {
// SAFETY: This pointer was allocated at creation time so we know it is valid. We know we
// have unique ownership and therefore it's safe to make a mutable reference because
// `UniqueRc` owns the only strong reference to itself.
unsafe { &mut (*self.ptr.as_ptr()).value }
}
}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
unsafe impl<#[may_dangle] T> Drop for UniqueRc<T> {
fn drop(&mut self) {
unsafe {
// destroy the contained object
drop_in_place(DerefMut::deref_mut(self));

// remove the implicit "strong weak" pointer now that we've destroyed the contents.
self.ptr.as_ref().dec_weak();

if self.ptr.as_ref().weak() == 0 {
Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
}
}
}
}
45 changes: 45 additions & 0 deletions library/alloc/src/rc/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,48 @@ fn test_rc_cyclic_with_two_ref() {
assert_eq!(Rc::strong_count(&two_refs), 3);
assert_eq!(Rc::weak_count(&two_refs), 2);
}

#[test]
fn test_unique_rc_weak() {
let rc = UniqueRc::new(42);
let weak = UniqueRc::downgrade(&rc);
assert!(weak.upgrade().is_none());

let _rc = UniqueRc::into_rc(rc);
assert_eq!(*weak.upgrade().unwrap(), 42);
}

#[test]
fn test_unique_rc_drop_weak() {
let rc = UniqueRc::new(42);
let weak = UniqueRc::downgrade(&rc);
mem::drop(weak);

let rc = UniqueRc::into_rc(rc);
assert_eq!(*rc, 42);
}

#[test]
fn test_unique_rc_drops_contents() {
let mut dropped = false;
struct DropMe<'a>(&'a mut bool);
impl Drop for DropMe<'_> {
fn drop(&mut self) {
*self.0 = true;
}
}
{
let rc = UniqueRc::new(DropMe(&mut dropped));
drop(rc);
}
assert!(dropped);
}

#[test]
fn test_unique_rc_weak_clone_holding_ref() {
let mut v = UniqueRc::new(0u8);
let w = UniqueRc::downgrade(&v);
let r = &mut *v;
let _ = w.clone(); // touch weak count
*r = 123;
}