From 72433e179d203431c85164555e651c7d65bd93c7 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 16 May 2018 23:09:58 -0700 Subject: [PATCH 1/4] Add implementations for Any + Send + Sync Implement `is`, `downcast_ref`, `downcast_mut` and `Debug` for `Any + Send + Sync`. --- src/libcore/any.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/libcore/any.rs b/src/libcore/any.rs index a6ba53087ac30..d608fed443dc9 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -136,6 +136,13 @@ impl fmt::Debug for Any + Send { } } +#[stable(feature = "any_send_sync_methods", since = "1.28.0")] +impl fmt::Debug for Any + Send + Sync { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Any") + } +} + impl Any { /// Returns `true` if the boxed type is the same as `T`. /// @@ -325,6 +332,89 @@ impl Any+Send { } } +impl Any+Send+Sync { + /// Forwards to the method defined on the type `Any`. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// + /// fn is_string(s: &(Any + Send + Sync)) { + /// if s.is::() { + /// println!("It's a string!"); + /// } else { + /// println!("Not a string..."); + /// } + /// } + /// + /// fn main() { + /// is_string(&0); + /// is_string(&"cookie monster".to_string()); + /// } + /// ``` + #[stable(feature = "any_send_sync_methods", since = "1.28.0")] + #[inline] + pub fn is(&self) -> bool { + Any::is::(self) + } + + /// Forwards to the method defined on the type `Any`. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// + /// fn print_if_string(s: &(Any + Send)) { + /// if let Some(string) = s.downcast_ref::() { + /// println!("It's a string({}): '{}'", string.len(), string); + /// } else { + /// println!("Not a string..."); + /// } + /// } + /// + /// fn main() { + /// print_if_string(&0); + /// print_if_string(&"cookie monster".to_string()); + /// } + /// ``` + #[stable(feature = "any_send_sync_methods", since = "1.28.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + Any::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `Any`. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// + /// fn modify_if_u32(s: &mut (Any+ Send)) { + /// if let Some(num) = s.downcast_mut::() { + /// *num = 42; + /// } + /// } + /// + /// fn main() { + /// let mut x = 10u32; + /// let mut s = "starlord".to_string(); + /// + /// modify_if_u32(&mut x); + /// modify_if_u32(&mut s); + /// + /// assert_eq!(x, 42); + /// assert_eq!(&s, "starlord"); + /// } + /// ``` + #[stable(feature = "any_send_sync_methods", since = "1.28.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + Any::downcast_mut::(self) + } +} /////////////////////////////////////////////////////////////////////////////// // TypeID and its methods From 37f5cf563c2c039503e8e50e252f2c1b31d69268 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 17 May 2018 08:17:35 -0700 Subject: [PATCH 2/4] Implement `downcast` for `Arc` 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. --- src/liballoc/arc.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index f75132487849f..0795498f87f9d 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 { } } +impl Arc { + #[inline] + #[unstable(feature = "rc_downcast", issue = "44608")] + /// Attempt to downcast the `Arc` to a concrete type. + /// + /// # Examples + /// + /// ``` + /// #![feature(rc_downcast)] + /// use std::any::Any; + /// use std::sync::Arc; + /// + /// fn print_if_string(value: Arc) { + /// if let Ok(string) = value.downcast::() { + /// 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(self) -> Result, Self> + where + T: Any + Send + Sync + 'static, + { + if (*self).is::() { + unsafe { + let raw: *const ArcInner = self.ptr.as_ptr(); + mem::forget(self); + Ok(Arc { + ptr: NonNull::new_unchecked(raw as *const ArcInner as *mut _), + phantom: PhantomData, + }) + } + } else { + Err(self) + } + } +} + impl Weak { /// Constructs a new `Weak`, 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 = Arc::new(i32::max_value()); + let r2: Arc = Arc::new("abc"); + + assert!(r1.clone().downcast::().is_err()); + + let r1i32 = r1.downcast::(); + assert!(r1i32.is_ok()); + assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value())); + + assert!(r2.clone().downcast::().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")] From 0c7bf56d805d981bb003a20a4c6b1f6b29790881 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 17 May 2018 08:25:48 -0700 Subject: [PATCH 3/4] Update `Arc` and `Rc` to use `NonNull::cast` to cast the inner pointers This avoids an `unsafe` block in each case. --- src/liballoc/arc.rs | 11 +++-------- src/liballoc/rc.rs | 12 +++--------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 0795498f87f9d..4026b3ababa31 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -1001,14 +1001,9 @@ impl Arc { T: Any + Send + Sync + 'static, { if (*self).is::() { - unsafe { - let raw: *const ArcInner = self.ptr.as_ptr(); - mem::forget(self); - Ok(Arc { - ptr: NonNull::new_unchecked(raw as *const ArcInner as *mut _), - phantom: PhantomData, - }) - } + let ptr = self.ptr.cast::>(); + mem::forget(self); + Ok(Arc { ptr, phantom: PhantomData }) } else { Err(self) } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 1648fc6b7ef4b..553c8b5ca3280 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -644,15 +644,9 @@ impl Rc { /// ``` pub fn downcast(self) -> Result, Rc> { if (*self).is::() { - // avoid the pointer arithmetic in from_raw - unsafe { - let raw: *const RcBox = self.ptr.as_ptr(); - forget(self); - Ok(Rc { - ptr: NonNull::new_unchecked(raw as *const RcBox as *mut _), - phantom: PhantomData, - }) - } + let ptr = self.ptr.cast::>(); + forget(self); + Ok(Rc { ptr, phantom: PhantomData }) } else { Err(self) } From 87c3d7bee52fb99f922c77fa267729b7898bc9a6 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 18 May 2018 08:18:44 -0700 Subject: [PATCH 4/4] Fix up Any doc examples Make the Any+Send+Sync examples use the right trait bounds, and fix a small whitespace issue. --- src/libcore/any.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcore/any.rs b/src/libcore/any.rs index d608fed443dc9..4437c36c15a5b 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -308,7 +308,7 @@ impl Any+Send { /// ``` /// use std::any::Any; /// - /// fn modify_if_u32(s: &mut (Any+ Send)) { + /// fn modify_if_u32(s: &mut (Any + Send)) { /// if let Some(num) = s.downcast_mut::() { /// *num = 42; /// } @@ -366,7 +366,7 @@ impl Any+Send+Sync { /// ``` /// use std::any::Any; /// - /// fn print_if_string(s: &(Any + Send)) { + /// fn print_if_string(s: &(Any + Send + Sync)) { /// if let Some(string) = s.downcast_ref::() { /// println!("It's a string({}): '{}'", string.len(), string); /// } else { @@ -392,7 +392,7 @@ impl Any+Send+Sync { /// ``` /// use std::any::Any; /// - /// fn modify_if_u32(s: &mut (Any+ Send)) { + /// fn modify_if_u32(s: &mut (Any + Send + Sync)) { /// if let Some(num) = s.downcast_mut::() { /// *num = 42; /// }