From 758a0ce934510b4e77586d02bc30a3d1d5d63cae Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 2 Sep 2017 16:28:48 +0200 Subject: [PATCH 1/2] alloc: Implement downcast Rc -> Rc Implement downcast the like it exists for Box. The implementation avoids using into_raw/from_raw, because the pointer arithmetic which should cancel does not seem to optimize out at the moment. Since Rc is never Send, only Rc and not Rc implements downcast. --- src/liballoc/rc.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 47f537caf31c4..814be059da7c0 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -244,6 +244,7 @@ use boxed::Box; #[cfg(test)] use std::boxed::Box; +use core::any::Any; use core::borrow; use core::cell::Cell; use core::cmp::Ordering; @@ -608,6 +609,46 @@ impl Rc { } } +impl Rc { + #[inline] + #[unstable(feature = "rc_downcast", issue = "0")] + /// Attempt to downcast the `Rc` to a concrete type. + /// + /// # Examples + /// + /// ``` + /// #![feature(rc_downcast)] + /// use std::any::Any; + /// use std::rc::Rc; + /// + /// fn print_if_string(value: Rc) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// fn main() { + /// let my_string = "Hello World".to_string(); + /// print_if_string(Rc::new(my_string)); + /// print_if_string(Rc::new(0i8)); + /// } + /// ``` + 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: Shared::new_unchecked(raw as *const RcBox as *mut _), + }) + } + } else { + Err(self) + } + } +} + impl Rc { // Allocates an `RcBox` with sufficient space for an unsized value unsafe fn allocate_for_ptr(ptr: *const T) -> *mut RcBox { @@ -1696,6 +1737,26 @@ mod tests { assert_eq!(&r[..], [1, 2, 3]); } + + #[test] + fn test_downcast() { + use std::any::Any; + + let r1: Rc = Rc::new(i32::max_value()); + let r2: Rc = Rc::new("abc"); + + assert!(r1.clone().downcast::().is_err()); + + let r1i32 = r1.downcast::(); + assert!(r1i32.is_ok()); + assert_eq!(r1i32.unwrap(), Rc::new(i32::max_value())); + + assert!(r2.clone().downcast::().is_err()); + + let r2str = r2.downcast::<&'static str>(); + assert!(r2str.is_ok()); + assert_eq!(r2str.unwrap(), Rc::new("abc")); + } } #[stable(feature = "rust1", since = "1.0.0")] From 3a39d95330623d47bcfcd5cac2d6b3c30e12ae5a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Fri, 15 Sep 2017 19:16:22 +0200 Subject: [PATCH 2/2] alloc: Add tracking issue for rc_downcast --- src/liballoc/rc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 814be059da7c0..58c589697f41c 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -611,7 +611,7 @@ impl Rc { impl Rc { #[inline] - #[unstable(feature = "rc_downcast", issue = "0")] + #[unstable(feature = "rc_downcast", issue = "44608")] /// Attempt to downcast the `Rc` to a concrete type. /// /// # Examples