Skip to content

Commit b834830

Browse files
committed
Auto merge of #195 - stepancheg:const, r=Amanieu
Make with_hasher functions const fn Sometimes it's useful to put a hashmap into a static variable. Unfortunately, this diff undoes #116: const expression `&STATIC_VAR` is not yet stable.
2 parents aa8c30b + 723d8ad commit b834830

File tree

5 files changed

+66
-19
lines changed

5 files changed

+66
-19
lines changed

src/map.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ impl<K, V, S> HashMap<K, V, S> {
275275
///
276276
/// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html
277277
#[cfg_attr(feature = "inline-more", inline)]
278-
pub fn with_hasher(hash_builder: S) -> Self {
278+
pub const fn with_hasher(hash_builder: S) -> Self {
279279
Self {
280280
hash_builder,
281281
table: RawTable::new(),
@@ -4500,4 +4500,28 @@ mod test_map {
45004500
assert_eq!(m.table.len(), left);
45014501
}
45024502
}
4503+
4504+
#[test]
4505+
fn test_const_with_hasher() {
4506+
use core::hash::BuildHasher;
4507+
use std::borrow::ToOwned;
4508+
use std::collections::hash_map::DefaultHasher;
4509+
4510+
#[derive(Clone)]
4511+
struct MyHasher;
4512+
impl BuildHasher for MyHasher {
4513+
type Hasher = DefaultHasher;
4514+
4515+
fn build_hasher(&self) -> DefaultHasher {
4516+
DefaultHasher::new()
4517+
}
4518+
}
4519+
4520+
const EMPTY_MAP: HashMap<u32, std::string::String, MyHasher> =
4521+
HashMap::with_hasher(MyHasher);
4522+
4523+
let mut map = EMPTY_MAP.clone();
4524+
map.insert(17, "seventeen".to_owned());
4525+
assert_eq!("seventeen", map[&17]);
4526+
}
45034527
}

src/raw/generic.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,20 @@ impl Group {
4747
pub const WIDTH: usize = mem::size_of::<Self>();
4848

4949
/// Returns a full group of empty bytes, suitable for use as the initial
50-
/// value for an empty hash table. This value is explicitly declared as
51-
/// a static variable to ensure the address is consistent across dylibs.
50+
/// value for an empty hash table.
5251
///
5352
/// This is guaranteed to be aligned to the group size.
54-
#[inline]
55-
pub fn static_empty() -> &'static [u8] {
56-
union AlignedBytes {
57-
_align: Group,
53+
pub const fn static_empty() -> &'static [u8; Group::WIDTH] {
54+
#[repr(C)]
55+
struct AlignedBytes {
56+
_align: [Group; 0],
5857
bytes: [u8; Group::WIDTH],
5958
};
60-
static ALIGNED_BYTES: AlignedBytes = AlignedBytes {
59+
const ALIGNED_BYTES: AlignedBytes = AlignedBytes {
60+
_align: [],
6161
bytes: [EMPTY; Group::WIDTH],
6262
};
63-
unsafe { &ALIGNED_BYTES.bytes }
63+
&ALIGNED_BYTES.bytes
6464
}
6565

6666
/// Loads a group of bytes starting at the given address.

src/raw/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,10 @@ impl<T> RawTable<T> {
382382
/// leave the data pointer dangling since that bucket is never written to
383383
/// due to our load factor forcing us to always have at least 1 free bucket.
384384
#[cfg_attr(feature = "inline-more", inline)]
385-
pub fn new() -> Self {
385+
pub const fn new() -> Self {
386386
Self {
387387
// Be careful to cast the entire slice to a raw pointer.
388-
ctrl: unsafe { NonNull::new_unchecked(Group::static_empty().as_ptr() as *mut u8) },
388+
ctrl: unsafe { NonNull::new_unchecked(Group::static_empty() as *const _ as *mut u8) },
389389
bucket_mask: 0,
390390
items: 0,
391391
growth_left: 0,

src/raw/sse2.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,20 @@ impl Group {
2525
pub const WIDTH: usize = mem::size_of::<Self>();
2626

2727
/// Returns a full group of empty bytes, suitable for use as the initial
28-
/// value for an empty hash table. This value is explicitly declared as
29-
/// a static variable to ensure the address is consistent across dylibs.
28+
/// value for an empty hash table.
3029
///
3130
/// This is guaranteed to be aligned to the group size.
32-
pub fn static_empty() -> &'static [u8] {
33-
union AlignedBytes {
34-
_align: Group,
31+
pub const fn static_empty() -> &'static [u8; Group::WIDTH] {
32+
#[repr(C)]
33+
struct AlignedBytes {
34+
_align: [Group; 0],
3535
bytes: [u8; Group::WIDTH],
3636
};
37-
static ALIGNED_BYTES: AlignedBytes = AlignedBytes {
37+
const ALIGNED_BYTES: AlignedBytes = AlignedBytes {
38+
_align: [],
3839
bytes: [EMPTY; Group::WIDTH],
3940
};
40-
unsafe { &ALIGNED_BYTES.bytes }
41+
&ALIGNED_BYTES.bytes
4142
}
4243

4344
/// Loads a group of bytes starting at the given address.

src/set.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ impl<T, S> HashSet<T, S> {
197197
///
198198
/// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html
199199
#[cfg_attr(feature = "inline-more", inline)]
200-
pub fn with_hasher(hasher: S) -> Self {
200+
pub const fn with_hasher(hasher: S) -> Self {
201201
Self {
202202
map: HashMap::with_hasher(hasher),
203203
}
@@ -2096,4 +2096,26 @@ mod test_set {
20962096
assert_eq!(set.len(), 4, "Removes non-matching items on drop");
20972097
}
20982098
}
2099+
2100+
#[test]
2101+
fn test_const_with_hasher() {
2102+
use core::hash::BuildHasher;
2103+
use std::collections::hash_map::DefaultHasher;
2104+
2105+
#[derive(Clone)]
2106+
struct MyHasher;
2107+
impl BuildHasher for MyHasher {
2108+
type Hasher = DefaultHasher;
2109+
2110+
fn build_hasher(&self) -> DefaultHasher {
2111+
DefaultHasher::new()
2112+
}
2113+
}
2114+
2115+
const EMPTY_SET: HashSet<u32, MyHasher> = HashSet::with_hasher(MyHasher);
2116+
2117+
let mut set = EMPTY_SET.clone();
2118+
set.insert(19);
2119+
assert!(set.contains(&19));
2120+
}
20992121
}

0 commit comments

Comments
 (0)