Refactor to avoid a spurious compatibility warning

Explained in the SAFETY comment. I’m not happy about *doing* this, but
it will make *using* this crate easier, since future-compatibility lints
make noise on bin crate builds, so this was polluting other people’s
code and making life harder for users.

I have traded one evil (a spurious warning) for another (unsafe code).
This commit is contained in:
Chris Morgan 2022-01-25 13:48:23 +11:00
parent 0656f18289
commit 521fbfe6bc
3 changed files with 24 additions and 30 deletions

View file

@ -9,6 +9,9 @@
- Implement `Default` on `Map` (not just on `RawMap`) - Implement `Default` on `Map` (not just on `RawMap`)
- Worked around the spurious `where_clauses_object_safety` future-compatibility lint that has been raised since mid-2018.
If you put `#![allow(where_clauses_object_safety)]` on your binary crates for this reason, you can remove it.
I dont plan for there to be any real changes from 0.12.1; I dont plan for there to be any real changes from 0.12.1;
it should be just a bit of housecleaning and a version bump. it should be just a bit of housecleaning and a version bump.

View file

@ -18,6 +18,8 @@ This library uses a fair bit of unsafe code for several reasons:
- 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). - 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).
- In the `Clone` implementation of `dyn CloneAny` with `Send` and/or `Sync` auto traits added, an unsafe block is used where safe code used to be used in order to avoid a [spurious future-compatibility lint](https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013).
Its not possible to remove all unsafety from this library without also removing some of the functionality. Still, at the cost of the `CloneAny` functionality, the raw interface and maybe the concurrency support, you can definitely remove all unsafe code. Heres how you could do it: Its not possible to remove all unsafety from this library without also removing some of the functionality. Still, at the cost of the `CloneAny` functionality, the raw interface and maybe the concurrency support, you can definitely remove all unsafe code. Heres how you could do it:
- Remove the genericness of it all; - Remove the genericness of it all;

View file

@ -10,15 +10,6 @@ use std::any::Any as StdAny;
pub trait CloneToAny { pub trait CloneToAny {
/// Clone `self` into a new `Box<dyn CloneAny>` object. /// Clone `self` into a new `Box<dyn CloneAny>` object.
fn clone_to_any(&self) -> Box<dyn CloneAny>; fn clone_to_any(&self) -> Box<dyn CloneAny>;
/// 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<dyn CloneAny + Sync>` object.
fn clone_to_any_sync(&self) -> Box<dyn CloneAny + Sync> where Self: 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 { impl<T: Any + Clone> CloneToAny for T {
@ -26,29 +17,27 @@ impl<T: Any + Clone> CloneToAny for T {
fn clone_to_any(&self) -> Box<dyn CloneAny> { fn clone_to_any(&self) -> Box<dyn CloneAny> {
Box::new(self.clone()) Box::new(self.clone())
} }
#[inline]
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<dyn CloneAny + Sync> where Self: Sync {
Box::new(self.clone())
}
#[inline]
fn clone_to_any_send_sync(&self) -> Box<dyn CloneAny + Send + Sync> where Self: Send + Sync {
Box::new(self.clone())
}
} }
macro_rules! impl_clone { macro_rules! impl_clone {
($t:ty, $method:ident) => { ($t:ty) => {
impl Clone for Box<$t> { impl Clone for Box<$t> {
#[inline] #[inline]
fn clone(&self) -> Box<$t> { fn clone(&self) -> Box<$t> {
(**self).$method() // SAFETY: this dance is to reapply any Send/Sync marker. Im not happy about this
// approach, given that I used to do it in safe code, but then came a dodgy
// future-compatibility warning where_clauses_object_safety, which is spurious for
// auto traits but still super annoying (future-compatibility lints seem to mean
// your bin crate needs a corresponding allow!). Although I explained my plight¹
// and it was all explained and agreed upon, no action has been taken. So I finally
// caved and worked around it by doing it this way, which matches whats done for
// std::any², so its probably not *too* bad.
//
// ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013
// ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616
let clone: Box<dyn CloneAny> = (**self).clone_to_any();
let raw: *mut dyn CloneAny = Box::into_raw(clone);
unsafe { Box::from_raw(raw as *mut $t) }
} }
} }
} }
@ -144,7 +133,7 @@ implement!(CloneAny,);
implement!(CloneAny, + Send); implement!(CloneAny, + Send);
implement!(CloneAny, + Sync); implement!(CloneAny, + Sync);
implement!(CloneAny, + Send + Sync); implement!(CloneAny, + Send + Sync);
impl_clone!(dyn CloneAny, clone_to_any); impl_clone!(dyn CloneAny);
impl_clone!(dyn CloneAny + Send, clone_to_any_send); impl_clone!(dyn CloneAny + Send);
impl_clone!(dyn CloneAny + Sync, clone_to_any_sync); impl_clone!(dyn CloneAny + Sync);
impl_clone!(dyn CloneAny + Send + Sync, clone_to_any_send_sync); impl_clone!(dyn CloneAny + Send + Sync);