From 3ca10cb1b6a8e52559011cd43eaef140acc82966 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 17 Mar 2020 15:33:41 +0000 Subject: [PATCH] Future-proof specialization code --- src/macros.rs | 15 +++++++++++++++ src/map.rs | 44 +++++++++++++++++++++++--------------------- src/raw/mod.rs | 13 ++++--------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index e743094768..0279597313 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -52,3 +52,18 @@ macro_rules! cfg_if { $(#[$m] $it)* }; } + +// Helper macro for specialization. This also helps avoid parse errors if the +// default fn syntax for specialization changes in the future. +#[cfg(feature = "nightly")] +macro_rules! default_fn { + ($($tt:tt)*) => { + default $($tt)* + } +} +#[cfg(not(feature = "nightly"))] +macro_rules! default_fn { + ($($tt:tt)*) => { + $($tt)* + } +} diff --git a/src/map.rs b/src/map.rs index edb9401507..81b8af13c9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -206,32 +206,34 @@ impl Clone for HashMap { // want the table to have elements hashed with the wrong hash_builder. let hash_builder = source.hash_builder.clone(); - #[cfg(not(feature = "nightly"))] - { - self.table.clone_from(&source.table); - } - #[cfg(feature = "nightly")] - { - trait HashClone { - fn clone_from(&mut self, source: &Self, hash_builder: &S); - } - impl HashClone for HashMap { - default fn clone_from(&mut self, source: &Self, _hash_builder: &S) { + // For backward-compatibility reasons we can't make the Clone impl + // depend on K: Hash + Eq and S: BuildHasher. However we can exploit + // this using specialization, which allows us to reuse the existing + // storage of the current HashMap to insert the cloned elements into. + trait HashClone { + fn clone_from(&mut self, source: &Self, hash_builder: &S); + } + impl HashClone for HashMap { + #[cfg_attr(feature = "inline-more", inline)] + default_fn! { + fn clone_from(&mut self, source: &Self, _hash_builder: &S) { self.table.clone_from(&source.table); } } - impl HashClone for HashMap - where - K: Eq + Hash, - S: BuildHasher, - { - fn clone_from(&mut self, source: &Self, hash_builder: &S) { - self.table - .clone_from_with_hasher(&source.table, |x| make_hash(hash_builder, &x.0)); - } + } + #[cfg(feature = "nightly")] + impl HashClone for HashMap + where + K: Eq + Hash, + S: BuildHasher, + { + #[cfg_attr(feature = "inline-more", inline)] + fn clone_from(&mut self, source: &Self, hash_builder: &S) { + self.table + .clone_from_with_hasher(&source.table, |x| make_hash(hash_builder, &x.0)); } - HashClone::clone_from(self, source, &hash_builder); } + HashClone::clone_from(self, source, &hash_builder); // Update hash_builder only if we successfully cloned all elements. self.hash_builder = hash_builder; diff --git a/src/raw/mod.rs b/src/raw/mod.rs index f22a63d4e2..7231999d2a 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -1036,16 +1036,11 @@ trait RawTableClone { unsafe fn clone_from_spec(&mut self, source: &Self, on_panic: impl FnMut(&mut Self)); } impl RawTableClone for RawTable { - #[cfg(feature = "nightly")] #[cfg_attr(feature = "inline-more", inline)] - default unsafe fn clone_from_spec(&mut self, source: &Self, on_panic: impl FnMut(&mut Self)) { - self.clone_from_impl(source, on_panic); - } - - #[cfg(not(feature = "nightly"))] - #[cfg_attr(feature = "inline-more", inline)] - unsafe fn clone_from_spec(&mut self, source: &Self, on_panic: impl FnMut(&mut Self)) { - self.clone_from_impl(source, on_panic); + default_fn! { + unsafe fn clone_from_spec(&mut self, source: &Self, on_panic: impl FnMut(&mut Self)) { + self.clone_from_impl(source, on_panic); + } } } #[cfg(feature = "nightly")]