diff --git a/bounded-collections/CHANGELOG.md b/bounded-collections/CHANGELOG.md index a30fa96a..8198520b 100644 --- a/bounded-collections/CHANGELOG.md +++ b/bounded-collections/CHANGELOG.md @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog]. [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ +## [0.1.5] - 2023-02-13 +- Fixed `Hash` impl (previously it could not be used in practice, because the size bound was required to also implement `Hash`). + ## [0.1.4] - 2023-01-28 - Fixed unnecessary decoding and allocations for bounded types, when the decoded length is greater than the allowed bound. - Add `Hash` derivation (when `feature = "std"`) for bounded types. diff --git a/bounded-collections/Cargo.toml b/bounded-collections/Cargo.toml index ffd73967..e5ae0780 100644 --- a/bounded-collections/Cargo.toml +++ b/bounded-collections/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bounded-collections" -version = "0.1.4" +version = "0.1.5" authors = ["Parity Technologies "] license = "MIT OR Apache-2.0" homepage = "https://github.com/paritytech/parity-common" diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index f306fe95..de975801 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -29,7 +29,6 @@ use core::{borrow::Borrow, marker::PhantomData, ops::Deref}; /// /// Unlike a standard `BTreeMap`, there is an enforced upper limit to the number of items in the /// map. All internal operations ensure this bound is respected. -#[cfg_attr(feature = "std", derive(Hash))] #[derive(Encode, scale_info::TypeInfo)] #[scale_info(skip_type_params(S))] pub struct BoundedBTreeMap(BTreeMap, PhantomData); @@ -237,6 +236,15 @@ where } } +// Custom implementation of `Hash` since deriving it would require all generic bounds to also +// implement it. +#[cfg(feature = "std")] +impl std::hash::Hash for BoundedBTreeMap { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + impl PartialEq> for BoundedBTreeMap where BTreeMap: PartialEq, @@ -641,4 +649,16 @@ mod test { assert_eq!(Ok(b2), b1.try_map(|(_, v)| (v as u16).checked_mul(100_u16).ok_or("overflow"))); } + + // Just a test that structs containing `BoundedBTreeMap` can derive `Hash`. (This was broken + // when it was deriving `Hash`). + #[test] + #[cfg(feature = "std")] + fn container_can_derive_hash() { + #[derive(Hash)] + struct Foo { + bar: u8, + map: BoundedBTreeMap>, + } + } } diff --git a/bounded-collections/src/bounded_btree_set.rs b/bounded-collections/src/bounded_btree_set.rs index 654ae1b3..3322cb72 100644 --- a/bounded-collections/src/bounded_btree_set.rs +++ b/bounded-collections/src/bounded_btree_set.rs @@ -29,7 +29,6 @@ use core::{borrow::Borrow, marker::PhantomData, ops::Deref}; /// /// Unlike a standard `BTreeSet`, there is an enforced upper limit to the number of items in the /// set. All internal operations ensure this bound is respected. -#[cfg_attr(feature = "std", derive(Hash))] #[derive(Encode, scale_info::TypeInfo)] #[scale_info(skip_type_params(S))] pub struct BoundedBTreeSet(BTreeSet, PhantomData); @@ -176,6 +175,15 @@ where } } +// Custom implementation of `Hash` since deriving it would require all generic bounds to also +// implement it. +#[cfg(feature = "std")] +impl std::hash::Hash for BoundedBTreeSet { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + impl PartialEq> for BoundedBTreeSet where BTreeSet: PartialEq, @@ -502,4 +510,16 @@ mod test { let b2: Result>, _> = b1.iter().map(|k| k + 1).skip(2).try_collect(); assert!(b2.is_err()); } + + // Just a test that structs containing `BoundedBTreeSet` can derive `Hash`. (This was broken + // when it was deriving `Hash`). + #[test] + #[cfg(feature = "std")] + fn container_can_derive_hash() { + #[derive(Hash)] + struct Foo { + bar: u8, + set: BoundedBTreeSet>, + } + } } diff --git a/bounded-collections/src/bounded_vec.rs b/bounded-collections/src/bounded_vec.rs index 4bbe389f..33a192fe 100644 --- a/bounded-collections/src/bounded_vec.rs +++ b/bounded-collections/src/bounded_vec.rs @@ -40,7 +40,7 @@ use serde::{ /// /// As the name suggests, the length of the queue is always bounded. All internal operations ensure /// this bound is respected. -#[cfg_attr(feature = "std", derive(Hash, Serialize), serde(transparent))] +#[cfg_attr(feature = "std", derive(Serialize), serde(transparent))] #[derive(Encode, scale_info::TypeInfo)] #[scale_info(skip_type_params(S))] pub struct BoundedVec(pub(super) Vec, #[cfg_attr(feature = "std", serde(skip_serializing))] PhantomData); @@ -108,7 +108,6 @@ where /// A bounded slice. /// /// Similar to a `BoundedVec`, but not owned and cannot be decoded. -#[cfg_attr(feature = "std", derive(Hash))] #[derive(Encode)] pub struct BoundedSlice<'a, T, S>(pub(super) &'a [T], PhantomData); @@ -273,6 +272,15 @@ impl<'a, T, S> Deref for BoundedSlice<'a, T, S> { } } +// Custom implementation of `Hash` since deriving it would require all generic bounds to also +// implement it. +#[cfg(feature = "std")] +impl<'a, T: std::hash::Hash, S> std::hash::Hash for BoundedSlice<'a, T, S> { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + impl<'a, T, S> core::iter::IntoIterator for BoundedSlice<'a, T, S> { type Item = &'a T; type IntoIter = core::slice::Iter<'a, T>; @@ -703,6 +711,15 @@ impl> TruncateFrom> for BoundedVec { } } +// Custom implementation of `Hash` since deriving it would require all generic bounds to also +// implement it. +#[cfg(feature = "std")] +impl std::hash::Hash for BoundedVec { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + // It is okay to give a non-mutable reference of the inner vec to anyone. impl AsRef> for BoundedVec { fn as_ref(&self) -> &Vec { @@ -907,7 +924,7 @@ where #[cfg(all(test, feature = "std"))] mod test { use super::*; - use crate::{bounded_vec, ConstU32}; + use crate::{bounded_vec, ConstU32, ConstU8}; use codec::CompactLen; #[test] @@ -1289,4 +1306,17 @@ mod test { assert_eq!(bound, &unbound[..]); assert!(bound == &unbound[..]); } + + // Just a test that structs containing `BoundedVec` and `BoundedSlice` can derive `Hash`. (This was broken when + // they were deriving `Hash`). + #[test] + #[cfg(feature = "std")] + fn container_can_derive_hash() { + #[derive(Hash)] + struct Foo<'a> { + bar: u8, + slice: BoundedSlice<'a, usize, ConstU8<8>>, + map: BoundedVec>, + } + } }