From e7500988b5786f196847b98c43dca2612c3d672e Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Thu, 15 May 2014 00:23:36 +1000 Subject: [PATCH] Add methods for unchecked casting of Any objects. This is definitely unsafe behaviour, but for certain cases it can be useful, such as when boxing a value and immediately wanting a reference to the value as the appropriate type, or when one has already used `is` and is thus certain of the type and so wishes to skip the redundant check. It'd be handy if we could then implement the checked ones (`as_ref`, `as_mut_ref` and `move`) on the traits as default methods, but no can do there, because of the `is(self)` method calling: an implementation for the trait objects it will autoreborrow, but in a default implementation it will consume, so we can't do it that way without restructuring at least `is` to take `&self`, which may have other issues (?), and making `AnyMutRefExt` and `AnyOwnExt` extend `AnyRefExt`. --- src/libcore/any.rs | 43 +++++++++++++++++++++++++++++-------------- src/libstd/owned.rs | 28 +++++++++++++++++----------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 70cd46dcfa2b4..3086886a9d769 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -59,6 +59,10 @@ pub trait AnyRefExt<'a> { /// Returns some reference to the boxed value if it is of type `T`, or /// `None` if it isn't. fn as_ref(self) -> Option<&'a T>; + + /// Returns a reference to the boxed value which must be of type `T`. + /// This is as dangerous as `transmute`; you should almost always use `as_ref` instead. + unsafe fn as_ref_unchecked(self) -> &'a T; } impl<'a> AnyRefExt<'a> for &'a Any { @@ -77,41 +81,52 @@ impl<'a> AnyRefExt<'a> for &'a Any { #[inline] fn as_ref(self) -> Option<&'a T> { if self.is::() { - unsafe { - // Get the raw representation of the trait object - let to: TraitObject = transmute_copy(&self); - - // Extract the data pointer - Some(transmute(to.data)) - } + Some(unsafe { self.as_ref_unchecked() }) } else { None } } + + #[inline] + unsafe fn as_ref_unchecked(self) -> &'a T { + // Get the raw representation of the trait object + let to: TraitObject = transmute_copy(&self); + + // Extract the data pointer + transmute(to.data) + } } /// Extension methods for a mutable referenced `Any` trait object pub trait AnyMutRefExt<'a> { /// Returns some mutable reference to the boxed value if it is of type `T`, or /// `None` if it isn't. + #[inline] fn as_mut(self) -> Option<&'a mut T>; + + /// Returns a mutable reference to the boxed value which must be of type `T`. + /// This is as dangerous as `transmute`; you should almost always use `as_mut` instead. + unsafe fn as_mut_unchecked(self) -> &'a mut T; } impl<'a> AnyMutRefExt<'a> for &'a mut Any { #[inline] fn as_mut(self) -> Option<&'a mut T> { if self.is::() { - unsafe { - // Get the raw representation of the trait object - let to: TraitObject = transmute_copy(&self); - - // Extract the data pointer - Some(transmute(to.data)) - } + Some(unsafe { self.as_mut_unchecked() }) } else { None } } + + #[inline] + unsafe fn as_mut_unchecked(self) -> &'a mut T { + // Get the raw representation of the trait object + let to: TraitObject = transmute_copy(&self); + + // Extract the data pointer + transmute(to.data) + } } #[cfg(test)] diff --git a/src/libstd/owned.rs b/src/libstd/owned.rs index 3af12c5154c29..43dd84fc26510 100644 --- a/src/libstd/owned.rs +++ b/src/libstd/owned.rs @@ -77,25 +77,31 @@ pub trait AnyOwnExt { /// Returns the boxed value if it is of type `T`, or /// `Err(Self)` if it isn't. fn move(self) -> Result, Self>; + + /// Returns the boxed value which must be of type `T`. + /// This is as dangerous as `transmute`; you should almost always use `move` instead. + unsafe fn move_unchecked(self) -> Box; } impl AnyOwnExt for Box { #[inline] fn move(self) -> Result, Box> { if self.is::() { - unsafe { - // Get the raw representation of the trait object - let to: TraitObject = - *mem::transmute::<&Box, &TraitObject>(&self); - - // Prevent destructor on self being run - intrinsics::forget(self); - - // Extract the data pointer - Ok(mem::transmute(to.data)) - } + Ok(unsafe { self.move_unchecked() }) } else { Err(self) } } + + #[inline] + unsafe fn move_unchecked(self) -> Box { + // Get the raw representation of the trait object + let to: TraitObject = *mem::transmute::<&Box, &TraitObject>(&self); + + // Prevent destructor on self being run + intrinsics::forget(self); + + // Extract the data pointer + mem::transmute(to.data) + } }