Skip to content

Commit

Permalink
Auto merge of #50836 - jsgf:arc-downcast, r=SimonSapin
Browse files Browse the repository at this point in the history
Arc downcast

Implement `downcast` for `Arc<Any + Send + Sync>` as part of #44608, and gated by the same `rc_downcast` feature.

This PR is mostly lightly-edited cut'n'paste.

This has two additional changes:
- The `downcast` implementation needs `Any + Send + Sync` implementations for `is` and `Debug`, and I added `downcast_ref` and `downcast_mut` for completeness/consistency. (Can these be insta-stabilized?)
- At @SimonSapin's suggestion, I converted `Arc` and `Rc` to use `NonNull::cast` to avoid an `unsafe` block in each which tidied things up nicely.
  • Loading branch information
bors committed Jun 1, 2018
2 parents efc508b + 87c3d7b commit b7a9d4e
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 10 deletions.
59 changes: 59 additions & 0 deletions src/liballoc/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -971,6 +972,44 @@ 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>() {
let ptr = self.ptr.cast::<ArcInner<T>>();
mem::forget(self);
Ok(Arc { ptr, 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`].
Expand Down Expand Up @@ -1844,6 +1883,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")]
Expand Down
12 changes: 3 additions & 9 deletions src/liballoc/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,15 +644,9 @@ impl Rc<Any> {
/// ```
pub fn downcast<T: Any>(self) -> Result<Rc<T>, Rc<Any>> {
if (*self).is::<T>() {
// avoid the pointer arithmetic in from_raw
unsafe {
let raw: *const RcBox<Any> = self.ptr.as_ptr();
forget(self);
Ok(Rc {
ptr: NonNull::new_unchecked(raw as *const RcBox<T> as *mut _),
phantom: PhantomData,
})
}
let ptr = self.ptr.cast::<RcBox<T>>();
forget(self);
Ok(Rc { ptr, phantom: PhantomData })
} else {
Err(self)
}
Expand Down
92 changes: 91 additions & 1 deletion src/libcore/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
///
Expand Down Expand Up @@ -301,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::<u32>() {
/// *num = 42;
/// }
Expand All @@ -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::<String>() {
/// 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<T: Any>(&self) -> bool {
Any::is::<T>(self)
}

/// Forwards to the method defined on the type `Any`.
///
/// # Examples
///
/// ```
/// use std::any::Any;
///
/// fn print_if_string(s: &(Any + Send + Sync)) {
/// if let Some(string) = s.downcast_ref::<String>() {
/// 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<T: Any>(&self) -> Option<&T> {
Any::downcast_ref::<T>(self)
}

/// Forwards to the method defined on the type `Any`.
///
/// # Examples
///
/// ```
/// use std::any::Any;
///
/// fn modify_if_u32(s: &mut (Any + Send + Sync)) {
/// if let Some(num) = s.downcast_mut::<u32>() {
/// *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<T: Any>(&mut self) -> Option<&mut T> {
Any::downcast_mut::<T>(self)
}
}

///////////////////////////////////////////////////////////////////////////////
// TypeID and its methods
Expand Down

0 comments on commit b7a9d4e

Please sign in to comment.