about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-02-04 18:42:13 +0100
committerGitHub <noreply@github.com>2022-02-04 18:42:13 +0100
commit6f03bd09ff6ef6402b68b32485f0d25e7f6c0c01 (patch)
treed63552075a2c9c2962cfea48482bf25911a20696
parent2fe9a32ed209b93ddd08cab174dfffefc1409a9c (diff)
parentf5e6d16d000e0aa73833de909de44e8dcc8589ed (diff)
downloadrust-6f03bd09ff6ef6402b68b32485f0d25e7f6c0c01.tar.gz
rust-6f03bd09ff6ef6402b68b32485f0d25e7f6c0c01.zip
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`?)
-rw-r--r--library/alloc/src/rc.rs35
-rw-r--r--library/alloc/src/sync.rs35
2 files changed, 70 insertions, 0 deletions
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 78bf28c843c..3065169e5e2 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -1203,6 +1203,41 @@ impl<T: Clone> Rc<T> {
         // reference to the allocation.
         unsafe { &mut this.ptr.as_mut().value }
     }
+
+    /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
+    /// clone.
+    ///
+    /// Assuming `rc_t` is of type `Rc<T>`, this function is functionally equivalent to
+    /// `(*rc_t).clone()`, but will avoid cloning the inner value where possible.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(arc_unwrap_or_clone)]
+    /// # use std::{ptr, rc::Rc};
+    /// let inner = String::from("test");
+    /// let ptr = inner.as_ptr();
+    ///
+    /// let rc = Rc::new(inner);
+    /// let inner = Rc::unwrap_or_clone(rc);
+    /// // The inner value was not cloned
+    /// assert!(ptr::eq(ptr, inner.as_ptr()));
+    ///
+    /// let rc = Rc::new(inner);
+    /// let rc2 = rc.clone();
+    /// let inner = Rc::unwrap_or_clone(rc);
+    /// // Because there were 2 references, we had to clone the inner value.
+    /// assert!(!ptr::eq(ptr, inner.as_ptr()));
+    /// // `rc2` is the last reference, so when we unwrap it we get back
+    /// // the original `String`.
+    /// let inner = Rc::unwrap_or_clone(rc2);
+    /// assert!(ptr::eq(ptr, inner.as_ptr()));
+    /// ```
+    #[inline]
+    #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")]
+    pub fn unwrap_or_clone(this: Self) -> T {
+        Rc::try_unwrap(this).unwrap_or_else(|rc| (*rc).clone())
+    }
 }
 
 impl Rc<dyn Any> {
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index 64f21d087da..7e7670aad64 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -1477,6 +1477,41 @@ impl<T: Clone> Arc<T> {
         // either unique to begin with, or became one upon cloning the contents.
         unsafe { Self::get_mut_unchecked(this) }
     }
+
+    /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
+    /// clone.
+    ///
+    /// Assuming `arc_t` is of type `Arc<T>`, this function is functionally equivalent to
+    /// `(*arc_t).clone()`, but will avoid cloning the inner value where possible.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(arc_unwrap_or_clone)]
+    /// # use std::{ptr, sync::Arc};
+    /// let inner = String::from("test");
+    /// let ptr = inner.as_ptr();
+    ///
+    /// let arc = Arc::new(inner);
+    /// let inner = Arc::unwrap_or_clone(arc);
+    /// // The inner value was not cloned
+    /// assert!(ptr::eq(ptr, inner.as_ptr()));
+    ///
+    /// let arc = Arc::new(inner);
+    /// let arc2 = arc.clone();
+    /// let inner = Arc::unwrap_or_clone(arc);
+    /// // Because there were 2 references, we had to clone the inner value.
+    /// assert!(!ptr::eq(ptr, inner.as_ptr()));
+    /// // `arc2` is the last reference, so when we unwrap it we get back
+    /// // the original `String`.
+    /// let inner = Arc::unwrap_or_clone(arc2);
+    /// assert!(ptr::eq(ptr, inner.as_ptr()));
+    /// ```
+    #[inline]
+    #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")]
+    pub fn unwrap_or_clone(this: Self) -> T {
+        Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone())
+    }
 }
 
 impl<T: ?Sized> Arc<T> {