Skip to content

Commit e3e4705

Browse files
authored
Rollup merge of rust-lang#75994 - mental32:impl-rc-new-cyclic, r=KodrAus
`impl Rc::new_cyclic` References rust-lang#75861 r? @Dylan-DPC
2 parents 8528e20 + 0f301e8 commit e3e4705

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

library/alloc/src/rc.rs

+44
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,50 @@ impl<T> Rc<T> {
325325
)
326326
}
327327

328+
/// Constructs a new `Rc<T>` using a weak reference to itself. Attempting
329+
/// to upgrade the weak reference before this function returns will result
330+
/// in a `None` value. However, the weak reference may be cloned freely and
331+
/// stored for use at a later time.
332+
#[unstable(feature = "arc_new_cyclic", issue = "75861")]
333+
pub fn new_cyclic(data_fn: impl FnOnce(&Weak<T>) -> T) -> Rc<T> {
334+
// Construct the inner in the "uninitialized" state with a single
335+
// weak reference.
336+
let uninit_ptr: NonNull<_> = Box::leak(box RcBox {
337+
strong: Cell::new(0),
338+
weak: Cell::new(1),
339+
value: mem::MaybeUninit::<T>::uninit(),
340+
})
341+
.into();
342+
343+
let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast();
344+
345+
let weak = Weak { ptr: init_ptr };
346+
347+
// It's important we don't give up ownership of the weak pointer, or
348+
// else the memory might be freed by the time `data_fn` returns. If
349+
// we really wanted to pass ownership, we could create an additional
350+
// weak pointer for ourselves, but this would result in additional
351+
// updates to the weak reference count which might not be necessary
352+
// otherwise.
353+
let data = data_fn(&weak);
354+
355+
unsafe {
356+
let inner = init_ptr.as_ptr();
357+
ptr::write(&raw mut (*inner).value, data);
358+
359+
let prev_value = (*inner).strong.get();
360+
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
361+
(*inner).strong.set(1);
362+
}
363+
364+
let strong = Rc::from_inner(init_ptr);
365+
366+
// Strong references should collectively own a shared weak reference,
367+
// so don't run the destructor for our old weak reference.
368+
mem::forget(weak);
369+
strong
370+
}
371+
328372
/// Constructs a new `Rc` with uninitialized contents.
329373
///
330374
/// # Examples

library/alloc/src/rc/tests.rs

+66
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,69 @@ fn test_array_from_slice() {
434434
let a: Result<Rc<[u32; 2]>, _> = r.clone().try_into();
435435
assert!(a.is_err());
436436
}
437+
438+
#[test]
439+
fn test_rc_cyclic_with_zero_refs() {
440+
struct ZeroRefs {
441+
inner: Weak<ZeroRefs>,
442+
}
443+
444+
let zero_refs = Rc::new_cyclic(|inner| {
445+
assert_eq!(inner.strong_count(), 0);
446+
assert!(inner.upgrade().is_none());
447+
ZeroRefs { inner: Weak::new() }
448+
});
449+
450+
assert_eq!(Rc::strong_count(&zero_refs), 1);
451+
assert_eq!(Rc::weak_count(&zero_refs), 0);
452+
assert_eq!(zero_refs.inner.strong_count(), 0);
453+
assert_eq!(zero_refs.inner.weak_count(), 0);
454+
}
455+
456+
#[test]
457+
fn test_rc_cyclic_with_one_ref() {
458+
struct OneRef {
459+
inner: Weak<OneRef>,
460+
}
461+
462+
let one_ref = Rc::new_cyclic(|inner| {
463+
assert_eq!(inner.strong_count(), 0);
464+
assert!(inner.upgrade().is_none());
465+
OneRef { inner: inner.clone() }
466+
});
467+
468+
assert_eq!(Rc::strong_count(&one_ref), 1);
469+
assert_eq!(Rc::weak_count(&one_ref), 1);
470+
471+
let one_ref2 = Weak::upgrade(&one_ref.inner).unwrap();
472+
assert!(Rc::ptr_eq(&one_ref, &one_ref2));
473+
474+
assert_eq!(one_ref.inner.strong_count(), 2);
475+
assert_eq!(one_ref.inner.weak_count(), 1);
476+
}
477+
478+
#[test]
479+
fn test_rc_cyclic_with_two_ref() {
480+
struct TwoRefs {
481+
inner: Weak<TwoRefs>,
482+
inner1: Weak<TwoRefs>,
483+
}
484+
485+
let two_refs = Rc::new_cyclic(|inner| {
486+
assert_eq!(inner.strong_count(), 0);
487+
assert!(inner.upgrade().is_none());
488+
TwoRefs { inner: inner.clone(), inner1: inner.clone() }
489+
});
490+
491+
assert_eq!(Rc::strong_count(&two_refs), 1);
492+
assert_eq!(Rc::weak_count(&two_refs), 2);
493+
494+
let two_ref3 = Weak::upgrade(&two_refs.inner).unwrap();
495+
assert!(Rc::ptr_eq(&two_refs, &two_ref3));
496+
497+
let two_ref2 = Weak::upgrade(&two_refs.inner1).unwrap();
498+
assert!(Rc::ptr_eq(&two_refs, &two_ref2));
499+
500+
assert_eq!(Rc::strong_count(&two_refs), 3);
501+
assert_eq!(Rc::weak_count(&two_refs), 2);
502+
}

0 commit comments

Comments
 (0)