No more bare trait objects: use dyn Trait syntax

This commit is contained in:
Chris Morgan 2022-01-25 12:57:35 +11:00
parent 7719a1c61b
commit bf29e608d9
4 changed files with 49 additions and 49 deletions

View file

@ -6,7 +6,7 @@ As another example of such an interface, JavaScript objects are exactly the same
Fortunately, we can do better than these things in Rust. Our type system is quite equal to easy, robust expression of such problems.
The ``AnyMap`` type is a friendly wrapper around a ``HashMap<TypeId, Box<Any>>``, exposing a nice, easy typed interface, perfectly safe and absolutely robust.
The ``AnyMap`` type is a friendly wrapper around a ``HashMap<TypeId, Box<dyn Any>>``, exposing a nice, easy typed interface, perfectly safe and absolutely robust.
What this means is that in an ``AnyMap`` you may store zero or one values for every type.
@ -14,7 +14,7 @@ What this means is that in an ``AnyMap`` you may store zero or one values for ev
This library uses a fair bit of unsafe code for several reasons:
- To support Any and CloneAny, unsafe code is required (because of how the `downcast` methods are defined in `impl Any` rather than being trait methods; I think this is kind of a historical detail of the structure of `std::any::Any`); if you wanted to ditch `Clone` support this unsafety could be removed.
- To support Any and CloneAny, unsafe code is required (because of how the `downcast` methods are defined in `impl dyn Any` rather than being trait methods; I think this is kind of a historical detail of the structure of `std::any::Any`); if you wanted to ditch `Clone` support this unsafety could be removed.
- In the interests of performance, skipping various checks that are unnecessary because of the invariants of the data structure (no need to check the type ID when its been statically ensured by being used as the hash map key) and simplifying hashing (type IDs are already good hashes, no need to mangle them through SipHash).

View file

@ -8,37 +8,37 @@ use std::any::Any as StdAny;
#[doc(hidden)]
pub trait CloneToAny {
/// Clone `self` into a new `Box<CloneAny>` object.
fn clone_to_any(&self) -> Box<CloneAny>;
/// Clone `self` into a new `Box<dyn CloneAny>` object.
fn clone_to_any(&self) -> Box<dyn CloneAny>;
/// Clone `self` into a new `Box<CloneAny + Send>` object.
fn clone_to_any_send(&self) -> Box<CloneAny + Send> where Self: Send;
/// Clone `self` into a new `Box<dyn CloneAny + Send>` object.
fn clone_to_any_send(&self) -> Box<dyn CloneAny + Send> where Self: Send;
/// Clone `self` into a new `Box<CloneAny + Sync>` object.
fn clone_to_any_sync(&self) -> Box<CloneAny + Sync> where Self: Sync;
/// Clone `self` into a new `Box<dyn CloneAny + Sync>` object.
fn clone_to_any_sync(&self) -> Box<dyn CloneAny + Sync> where Self: Sync;
/// Clone `self` into a new `Box<CloneAny + Send + Sync>` object.
fn clone_to_any_send_sync(&self) -> Box<CloneAny + Send + Sync> where Self: Send + Sync;
/// Clone `self` into a new `Box<dyn CloneAny + Send + Sync>` object.
fn clone_to_any_send_sync(&self) -> Box<dyn CloneAny + Send + Sync> where Self: Send + Sync;
}
impl<T: Any + Clone> CloneToAny for T {
#[inline]
fn clone_to_any(&self) -> Box<CloneAny> {
fn clone_to_any(&self) -> Box<dyn CloneAny> {
Box::new(self.clone())
}
#[inline]
fn clone_to_any_send(&self) -> Box<CloneAny + Send> where Self: Send {
fn clone_to_any_send(&self) -> Box<dyn CloneAny + Send> where Self: Send {
Box::new(self.clone())
}
#[inline]
fn clone_to_any_sync(&self) -> Box<CloneAny + Sync> where Self: Sync {
fn clone_to_any_sync(&self) -> Box<dyn CloneAny + Sync> where Self: Sync {
Box::new(self.clone())
}
#[inline]
fn clone_to_any_send_sync(&self) -> Box<CloneAny + Send + Sync> where Self: Send + Sync {
fn clone_to_any_send_sync(&self) -> Box<dyn CloneAny + Send + Sync> where Self: Send + Sync {
Box::new(self.clone())
}
}
@ -108,14 +108,14 @@ pub trait IntoBox<A: ?Sized + UncheckedAnyExt>: Any {
macro_rules! implement {
($base:ident, $(+ $bounds:ident)*) => {
impl fmt::Debug for $base $(+ $bounds)* {
impl fmt::Debug for dyn $base $(+ $bounds)* {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(stringify!($base $(+ $bounds)*))
f.pad(stringify!(dyn $base $(+ $bounds)*))
}
}
impl UncheckedAnyExt for $base $(+ $bounds)* {
impl UncheckedAnyExt for dyn $base $(+ $bounds)* {
#[inline]
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
&*(self as *const Self as *const T)
@ -132,9 +132,9 @@ macro_rules! implement {
}
}
impl<T: $base $(+ $bounds)*> IntoBox<$base $(+ $bounds)*> for T {
impl<T: $base $(+ $bounds)*> IntoBox<dyn $base $(+ $bounds)*> for T {
#[inline]
fn into_box(self) -> Box<$base $(+ $bounds)*> {
fn into_box(self) -> Box<dyn $base $(+ $bounds)*> {
Box::new(self)
}
}
@ -152,7 +152,7 @@ implement!(CloneAny, + Sync);
implement!(CloneAny, + Send + Sync);
define!(CloneAny);
impl_clone!(CloneAny, clone_to_any);
impl_clone!((CloneAny + Send), clone_to_any_send);
impl_clone!((CloneAny + Sync), clone_to_any_sync);
impl_clone!((CloneAny + Send + Sync), clone_to_any_send_sync);
impl_clone!(dyn CloneAny, clone_to_any);
impl_clone!(dyn CloneAny + Send, clone_to_any_send);
impl_clone!(dyn CloneAny + Sync, clone_to_any_sync);
impl_clone!(dyn CloneAny + Send + Sync, clone_to_any_send_sync);

View file

@ -95,7 +95,7 @@ pub mod raw;
/// be `anymap::any::Any`, but there are other choices:
///
/// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`.
/// - You can add on `+ Send` and/or `+ Sync` (e.g. `Map<Any + Send>`) to add those bounds.
/// - You can add on `+ Send` and/or `+ Sync` (e.g. `Map<dyn Any + Send>`) to add those bounds.
///
/// ```rust
/// # use anymap::AnyMap;
@ -120,7 +120,7 @@ pub mod raw;
///
/// Values containing non-static references are not permitted.
#[derive(Debug)]
pub struct Map<A: ?Sized + UncheckedAnyExt = Any> {
pub struct Map<A: ?Sized + UncheckedAnyExt = dyn Any> {
raw: RawMap<A>,
}
@ -139,7 +139,7 @@ impl<A: ?Sized + UncheckedAnyExt> Clone for Map<A> where Box<A>: Clone {
/// Why is this a separate type alias rather than a default value for `Map<A>`? `Map::new()`
/// doesnt seem to be happy to infer that it should go with the default value.
/// Its a bit sad, really. Ah well, I guess this approach will do.
pub type AnyMap = Map<Any>;
pub type AnyMap = Map<dyn Any>;
impl_common_methods! {
field: Map.raw;
@ -394,7 +394,7 @@ mod tests {
}
test_entry!(test_entry_any, AnyMap);
test_entry!(test_entry_cloneany, Map<CloneAny>);
test_entry!(test_entry_cloneany, Map<dyn CloneAny>);
#[test]
fn test_default() {
@ -404,7 +404,7 @@ mod tests {
#[test]
fn test_clone() {
let mut map: Map<CloneAny> = Map::new();
let mut map: Map<dyn CloneAny> = Map::new();
let _ = map.insert(A(1));
let _ = map.insert(B(2));
let _ = map.insert(D(3));
@ -428,25 +428,25 @@ mod tests {
fn assert_sync<T: Sync>() { }
fn assert_clone<T: Clone>() { }
fn assert_debug<T: ::std::fmt::Debug>() { }
assert_send::<Map<Any + Send>>();
assert_send::<Map<Any + Send + Sync>>();
assert_sync::<Map<Any + Sync>>();
assert_sync::<Map<Any + Send + Sync>>();
assert_debug::<Map<Any>>();
assert_debug::<Map<Any + Send>>();
assert_debug::<Map<Any + Sync>>();
assert_debug::<Map<Any + Send + Sync>>();
assert_send::<Map<CloneAny + Send>>();
assert_send::<Map<CloneAny + Send + Sync>>();
assert_sync::<Map<CloneAny + Sync>>();
assert_sync::<Map<CloneAny + Send + Sync>>();
assert_clone::<Map<CloneAny + Send>>();
assert_clone::<Map<CloneAny + Send + Sync>>();
assert_clone::<Map<CloneAny + Sync>>();
assert_clone::<Map<CloneAny + Send + Sync>>();
assert_debug::<Map<CloneAny>>();
assert_debug::<Map<CloneAny + Send>>();
assert_debug::<Map<CloneAny + Sync>>();
assert_debug::<Map<CloneAny + Send + Sync>>();
assert_send::<Map<dyn Any + Send>>();
assert_send::<Map<dyn Any + Send + Sync>>();
assert_sync::<Map<dyn Any + Sync>>();
assert_sync::<Map<dyn Any + Send + Sync>>();
assert_debug::<Map<dyn Any>>();
assert_debug::<Map<dyn Any + Send>>();
assert_debug::<Map<dyn Any + Sync>>();
assert_debug::<Map<dyn Any + Send + Sync>>();
assert_send::<Map<dyn CloneAny + Send>>();
assert_send::<Map<dyn CloneAny + Send + Sync>>();
assert_sync::<Map<dyn CloneAny + Sync>>();
assert_sync::<Map<dyn CloneAny + Send + Sync>>();
assert_clone::<Map<dyn CloneAny + Send>>();
assert_clone::<Map<dyn CloneAny + Send + Sync>>();
assert_clone::<Map<dyn CloneAny + Sync>>();
assert_clone::<Map<dyn CloneAny + Send + Sync>>();
assert_debug::<Map<dyn CloneAny>>();
assert_debug::<Map<dyn CloneAny + Send>>();
assert_debug::<Map<dyn CloneAny + Sync>>();
assert_debug::<Map<dyn CloneAny + Send + Sync>>();
}
}

View file

@ -56,7 +56,7 @@ fn type_id_hasher() {
/// contents of an `Map`. However, because you will then be dealing with `Any` trait objects, it
/// doesnt tend to be so very useful. Still, if you need it, its here.
#[derive(Debug)]
pub struct RawMap<A: ?Sized + UncheckedAnyExt = Any> {
pub struct RawMap<A: ?Sized + UncheckedAnyExt = dyn Any> {
inner: HashMap<TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>,
}