Skip to content

Add function Arc/Rc::as_weak(…) to convert &Arc/Rc to &Weak #100472

New issue

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

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

Already on GitHub? # to your account

Closed
wants to merge 7 commits into from
37 changes: 37 additions & 0 deletions library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ struct RcBox<T: ?Sized> {
#[cfg_attr(not(test), rustc_diagnostic_item = "Rc")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_insignificant_dtor]
#[repr(transparent)]
pub struct Rc<T: ?Sized> {
ptr: NonNull<RcBox<T>>,
phantom: PhantomData<RcBox<T>>,
Expand Down Expand Up @@ -933,6 +934,41 @@ impl<T: ?Sized> Rc<T> {
Weak { ptr: this.ptr }
}

/// Convert a reference to an [`Rc`] into a reference to a [`Weak`] of the same type.
///
/// This is a type-only operation; it doesn't modify the inner reference counts.
/// Therefore if you call [`Weak::weak_count()`] on the returned `&Weak<T>`,
/// it is possible to get a result of `0`, which is otherwise only possible
/// when there are no remaining strong references and the value has been dropped.
///
/// # Examples
///
/// ```
/// #![feature(rc_as_weak)]
///
/// use std::rc::{Rc, Weak};
///
/// let five: &Rc<i32> = &Rc::new(5);
///
/// let weak_five: &Weak<i32> = Rc::as_weak(five);
/// ```
#[inline]
#[unstable(feature = "rc_as_weak", issue = "100472")]
#[must_use]
pub const fn as_weak(this: &Self) -> &Weak<T> {
// SAFETY: `Rc<T>` and `Weak<T>` are guaranteed to have the same representation
// because both are `#[repr(transparent)]` with their only (non-ZST) field being
// being a `NonNull<RcBox<T>>`. The static guarantees carried by a `Weak<T>` (that
// the pointer will point to a live `RcBox<T>` allocation _unless_ it is the sentinel
// value of `usize::MAX`) are strictly weaker than those carried by an `Rc<T>` (that
// the pointer will always point to a live `RcBox<T>` allocation containing a live `T`).
// The different drop in their drop behaviour is not relevant because this function
// is only concerned with shared references, not owned values. Therefore no safety
// invariants are violated by interpreting an `&Rc<T>` as a `&Weak<T>`.
let weak = this as *const Self as *const Weak<T>;
unsafe { &*weak }
}

/// Gets the number of [`Weak`] pointers to this allocation.
///
/// # Examples
Expand Down Expand Up @@ -2143,6 +2179,7 @@ impl<T, I: iter::TrustedLen<Item = T>> ToRcSlice<T> for I {
///
/// [`upgrade`]: Weak::upgrade
#[stable(feature = "rc_weak", since = "1.4.0")]
#[repr(transparent)]
pub struct Weak<T: ?Sized> {
// This is a `NonNull` to allow optimizing the size of this type in enums,
// but it is not necessarily a valid pointer.
Expand Down
4 changes: 4 additions & 0 deletions library/alloc/src/rc/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ fn test_weak_count() {
let w = Rc::downgrade(&a);
assert!(Rc::strong_count(&a) == 1);
assert!(Rc::weak_count(&a) == 1);
let r: &Weak<i32> = Rc::as_weak(&a);
assert!(Rc::strong_count(&a) == 1);
assert!(Rc::weak_count(&a) == 1);
assert!(r.as_ptr() == Rc::as_ptr(&a));
drop(w);
assert!(Rc::strong_count(&a) == 1);
assert!(Rc::weak_count(&a) == 0);
Expand Down
37 changes: 37 additions & 0 deletions library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ macro_rules! acquire {
/// [rc_examples]: crate::rc#examples
#[cfg_attr(not(test), rustc_diagnostic_item = "Arc")]
#[stable(feature = "rust1", since = "1.0.0")]
#[repr(transparent)]
pub struct Arc<T: ?Sized> {
ptr: NonNull<ArcInner<T>>,
phantom: PhantomData<ArcInner<T>>,
Expand Down Expand Up @@ -282,6 +283,7 @@ impl<T: ?Sized> Arc<T> {
///
/// [`upgrade`]: Weak::upgrade
#[stable(feature = "arc_weak", since = "1.4.0")]
#[repr(transparent)]
pub struct Weak<T: ?Sized> {
// This is a `NonNull` to allow optimizing the size of this type in enums,
// but it is not necessarily a valid pointer.
Expand Down Expand Up @@ -959,6 +961,41 @@ impl<T: ?Sized> Arc<T> {
}
}

/// Convert a reference to an [`Arc`] into a reference to a [`Weak`] of the same type.
///
/// This is a type-only operation; it doesn't modify the inner reference counts.
/// Therefore if you call [`Weak::weak_count()`] on the returned `&Weak<T>`,
/// it is possible to get a result of `0`, which is otherwise only possible
/// when there are no remaining strong references and the value has been dropped.
///
/// # Examples
///
/// ```
/// #![feature(rc_as_weak)]
///
/// use std::sync::{Arc, Weak};
///
/// let five: &Arc<i32> = &Arc::new(5);
///
/// let weak_five: &Weak<i32> = Arc::as_weak(five);
/// ```
#[inline]
#[unstable(feature = "rc_as_weak", issue = "100472")]
#[must_use]
pub const fn as_weak(this: &Self) -> &Weak<T> {
// SAFETY: `Arc<T>` and `Weak<T>` are guaranteed to have the same representation
// because both are `#[repr(transparent)]` with their only (non-ZST) field being
// being a `NonNull<ArcInner<T>>`. The static guarantees carried by a `Weak<T>` (that
// the pointer will point to a live `ArcInner<T>` allocation _unless_ it is the sentinel
// value of `usize::MAX`) are strictly weaker than those carried by an `Arc<T>` (that
// the pointer will always point to a live `ArcInner<T>` allocation containing a live `T`).
// The different drop in their drop behaviour is not relevant because this function
// is only concerned with shared references, not owned values. Therefore no safety
// invariants are violated by interpreting an `&Arc<T>` as a `&Weak<T>`.
let weak = this as *const Self as *const Weak<T>;
unsafe { &*weak }
}

/// Gets the number of [`Weak`] pointers to this allocation.
///
/// # Safety
Expand Down
4 changes: 4 additions & 0 deletions library/alloc/src/sync/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ fn test_weak_count() {
let w = Arc::downgrade(&a);
assert!(Arc::strong_count(&a) == 1);
assert!(Arc::weak_count(&a) == 1);
let r: &Weak<i32> = Arc::as_weak(&a);
assert!(Arc::strong_count(&a) == 1);
assert!(Arc::weak_count(&a) == 1);
assert!(r.as_ptr() == Arc::as_ptr(&a));
let x = w.clone();
assert!(Arc::weak_count(&a) == 2);
drop(w);
Expand Down