Skip to content

Commit 6f03bd0

Browse files
authored
Rollup merge of #91589 - derekdreery:arc_unwrap_or_clone, r=m-ou-se
impl `Arc::unwrap_or_clone` The function gets the inner value, cloning only if necessary. The conversation started on [`irlo`](https://internals.rust-lang.org/t/arc-into-inner/15707). If the reviewer think the PR has potential to be merged, and does not need an RFC, then I will create the corresponding tracking issues and update the PR. ## Alternative names - `into_inner` - `make_owned` - `make_unique` - `take_*` (`take_inner`?)
2 parents 2fe9a32 + f5e6d16 commit 6f03bd0

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

library/alloc/src/rc.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,41 @@ impl<T: Clone> Rc<T> {
12031203
// reference to the allocation.
12041204
unsafe { &mut this.ptr.as_mut().value }
12051205
}
1206+
1207+
/// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
1208+
/// clone.
1209+
///
1210+
/// Assuming `rc_t` is of type `Rc<T>`, this function is functionally equivalent to
1211+
/// `(*rc_t).clone()`, but will avoid cloning the inner value where possible.
1212+
///
1213+
/// # Examples
1214+
///
1215+
/// ```
1216+
/// #![feature(arc_unwrap_or_clone)]
1217+
/// # use std::{ptr, rc::Rc};
1218+
/// let inner = String::from("test");
1219+
/// let ptr = inner.as_ptr();
1220+
///
1221+
/// let rc = Rc::new(inner);
1222+
/// let inner = Rc::unwrap_or_clone(rc);
1223+
/// // The inner value was not cloned
1224+
/// assert!(ptr::eq(ptr, inner.as_ptr()));
1225+
///
1226+
/// let rc = Rc::new(inner);
1227+
/// let rc2 = rc.clone();
1228+
/// let inner = Rc::unwrap_or_clone(rc);
1229+
/// // Because there were 2 references, we had to clone the inner value.
1230+
/// assert!(!ptr::eq(ptr, inner.as_ptr()));
1231+
/// // `rc2` is the last reference, so when we unwrap it we get back
1232+
/// // the original `String`.
1233+
/// let inner = Rc::unwrap_or_clone(rc2);
1234+
/// assert!(ptr::eq(ptr, inner.as_ptr()));
1235+
/// ```
1236+
#[inline]
1237+
#[unstable(feature = "arc_unwrap_or_clone", issue = "93610")]
1238+
pub fn unwrap_or_clone(this: Self) -> T {
1239+
Rc::try_unwrap(this).unwrap_or_else(|rc| (*rc).clone())
1240+
}
12061241
}
12071242

12081243
impl Rc<dyn Any> {

library/alloc/src/sync.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,41 @@ impl<T: Clone> Arc<T> {
14771477
// either unique to begin with, or became one upon cloning the contents.
14781478
unsafe { Self::get_mut_unchecked(this) }
14791479
}
1480+
1481+
/// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
1482+
/// clone.
1483+
///
1484+
/// Assuming `arc_t` is of type `Arc<T>`, this function is functionally equivalent to
1485+
/// `(*arc_t).clone()`, but will avoid cloning the inner value where possible.
1486+
///
1487+
/// # Examples
1488+
///
1489+
/// ```
1490+
/// #![feature(arc_unwrap_or_clone)]
1491+
/// # use std::{ptr, sync::Arc};
1492+
/// let inner = String::from("test");
1493+
/// let ptr = inner.as_ptr();
1494+
///
1495+
/// let arc = Arc::new(inner);
1496+
/// let inner = Arc::unwrap_or_clone(arc);
1497+
/// // The inner value was not cloned
1498+
/// assert!(ptr::eq(ptr, inner.as_ptr()));
1499+
///
1500+
/// let arc = Arc::new(inner);
1501+
/// let arc2 = arc.clone();
1502+
/// let inner = Arc::unwrap_or_clone(arc);
1503+
/// // Because there were 2 references, we had to clone the inner value.
1504+
/// assert!(!ptr::eq(ptr, inner.as_ptr()));
1505+
/// // `arc2` is the last reference, so when we unwrap it we get back
1506+
/// // the original `String`.
1507+
/// let inner = Arc::unwrap_or_clone(arc2);
1508+
/// assert!(ptr::eq(ptr, inner.as_ptr()));
1509+
/// ```
1510+
#[inline]
1511+
#[unstable(feature = "arc_unwrap_or_clone", issue = "93610")]
1512+
pub fn unwrap_or_clone(this: Self) -> T {
1513+
Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone())
1514+
}
14801515
}
14811516

14821517
impl<T: ?Sized> Arc<T> {

0 commit comments

Comments
 (0)