about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJeremy Fitzhardinge <jsgf@fb.com>2018-05-17 08:17:35 -0700
committerJeremy Fitzhardinge <jsgf@fb.com>2018-05-31 13:27:08 -0700
commit37f5cf563c2c039503e8e50e252f2c1b31d69268 (patch)
tree77eef669544c42a7876ffa3c5d7f529c528c86d8
parent72433e179d203431c85164555e651c7d65bd93c7 (diff)
downloadrust-37f5cf563c2c039503e8e50e252f2c1b31d69268.tar.gz
rust-37f5cf563c2c039503e8e50e252f2c1b31d69268.zip
Implement `downcast` for `Arc<Any + Send + Sync>`
We only need to implement it for `Any + Send + Sync` because in practice
that's the only useful combination for `Arc` and `Any`.

Implementation for #44608 under the `rc_downcast` feature.
-rw-r--r--src/liballoc/arc.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index f7513248784..0795498f87f 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -16,6 +16,7 @@
 //!
 //! [arc]: struct.Arc.html
 
+use core::any::Any;
 use core::sync::atomic;
 use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
 use core::borrow;
@@ -971,6 +972,49 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc<T> {
     }
 }
 
+impl Arc<Any + Send + Sync> {
+    #[inline]
+    #[unstable(feature = "rc_downcast", issue = "44608")]
+    /// Attempt to downcast the `Arc<Any + Send + Sync>` to a concrete type.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(rc_downcast)]
+    /// use std::any::Any;
+    /// use std::sync::Arc;
+    ///
+    /// fn print_if_string(value: Arc<Any + Send + Sync>) {
+    ///     if let Ok(string) = value.downcast::<String>() {
+    ///         println!("String ({}): {}", string.len(), string);
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     let my_string = "Hello World".to_string();
+    ///     print_if_string(Arc::new(my_string));
+    ///     print_if_string(Arc::new(0i8));
+    /// }
+    /// ```
+    pub fn downcast<T>(self) -> Result<Arc<T>, Self>
+    where
+        T: Any + Send + Sync + 'static,
+    {
+        if (*self).is::<T>() {
+            unsafe {
+                let raw: *const ArcInner<Any + Send + Sync> = self.ptr.as_ptr();
+                mem::forget(self);
+                Ok(Arc {
+                    ptr: NonNull::new_unchecked(raw as *const ArcInner<T> as *mut _),
+                    phantom: PhantomData,
+                })
+            }
+        } else {
+            Err(self)
+        }
+    }
+}
+
 impl<T> Weak<T> {
     /// Constructs a new `Weak<T>`, allocating memory for `T` without initializing
     /// it. Calling [`upgrade`] on the return value always gives [`None`].
@@ -1844,6 +1888,26 @@ mod tests {
 
         assert_eq!(&r[..], [1, 2, 3]);
     }
+
+    #[test]
+    fn test_downcast() {
+        use std::any::Any;
+
+        let r1: Arc<Any + Send + Sync> = Arc::new(i32::max_value());
+        let r2: Arc<Any + Send + Sync> = Arc::new("abc");
+
+        assert!(r1.clone().downcast::<u32>().is_err());
+
+        let r1i32 = r1.downcast::<i32>();
+        assert!(r1i32.is_ok());
+        assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value()));
+
+        assert!(r2.clone().downcast::<i32>().is_err());
+
+        let r2str = r2.downcast::<&'static str>();
+        assert!(r2str.is_ok());
+        assert_eq!(r2str.unwrap(), Arc::new("abc"));
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]