Skip to content

Explore possibilities for hashmap! and hashset! to be generic over hashers #22

@hcpl

Description

@hcpl

Motivation

Hash{Map,Set}::with_capacity limit us to the default hashing algorithm (SipHash).

Solutions

I have 2 ideas on how to implement this suggestion.

Extend capabilities of hashmap! and hashset!

I tried to trivially modify hashmap! like this:

 macro_rules! hashmap {
     (@single $($x:tt)*) => (());
     (@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap!(@single $rest)),*]));

     ($($key:expr => $value:expr,)+) => { hashmap!($($key => $value),+) };
     ($($key:expr => $value:expr),*) => {
         {
             let _cap = hashmap!(@count $($key),*);
-            let mut _map = ::std::collections::HashMap::with_capacity(_cap);
+            let mut _map = ::std::collections::HashMap::with_capacity_and_hasher(_cap, Default::default());
             $(
                 let _ = _map.insert($key, $value);
             )*
             _map
         }
     };
 }

For hashset! changes are analogous.

Unfortunately, this change messes with type inference:

error[E0282]: unable to infer enough type information about `S`
 --> src/main.rs:5:15
  |
5 |       let map = hashmap! {
  |  _______________^ starting here...
6 | |         "a" => 1,
7 | |         "b" => 3,
8 | |         "c" => 2,
9 | |     };
  | |_____^ ...ending here: cannot infer type for `S`
  |
  = note: type annotations or generic parameter binding required
  = note: this error originates in a macro outside of the current crate

Aside from the fact that this is a breaking change (I bet most of the code that uses these macros relies on type inference to remove boilerplate), this change also requires users to always write down the exact type which is unergonomic.

If anyone has a better idea how to approach this problem without creating new macros, please comment!

Add new macros

  • hashmap_with_hasher! --- create a HashMap with a provided BuildHasher
  • hashmap_with_default_hasher! --- create a HashMap with a default BuildHasher
  • hashset_with_hasher! --- create a HashSet with a provided BuildHasher
  • hashset_with_default_hasher! --- create a HashSet with a default BuildHasher

hash{map,set}_with_default_hasher! work as hash{map,set}! but use S::default() where S: BuildHasher + Default instead of RandomState::default().

Syntax of hash{map,set}_with_hasher! are subject for bikeshedding:

// 1
hashmap_with_hasher!(FnvHasher::with_key(0), {
    "a" => 1,
    "b" => 2,
});

hashset_with_hasher!(FnvHasher::with_key(0), {"a", "b"});

// 2
hashmap_with_hasher! {
    hasher = FnvHasher::with_key(0),
    data = {
        "a" => 1,
        "b" => 2,
    },
};

hashset_with_hasher! {
    hasher = FnvHasher::with_key(0),
    data = {"a", "b"},
};

// Additional syntaxes are welcome!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions