Exposing with_capacity with custom hasher?
Closed this issue · 3 comments
As far as I can tell/understand, it looks like the with_capacity
function is available only when using a default std
IndexMap
using RandomState
. However, I'm not deeply familiar with this part of Rust, but I believe it should be possible to expose a version of with_capacity
with any hasher given in a core::hash::BuildHasherDefault
, for example:
pub type AHashIndexMap<K, V> = indexmap::IndexMap<K, V, core::hash::BuildHasherDefault<ahash::AHasher>>;
I believe hashbrown exposes a with_capacity
method in these situations. Does indexmap do something similar? If not, it would be a handy option.
In hashbrown
, the default hasher is ahash
, and that's the only one that supports with_capacity
. Notice that its impl
block is only for DefaultHashBuilder
, which is an alias for BuildHasherDefault<AHasher>
. In indexmap
, we've chosen to stick with the standard library's default hasher, and again that's the only one that supports with_capacity
.
The reasons for that not being more generic are similar to the discussion of new
in #265, although unfortunately there's not such an easy alternative like IndexMap::default
when you want capacity. You can either use default
and then reserve
, or use something like with_capacity_and_hasher(cap, AHasher::default())
.
Maybe there could be a new method like default_with_capacity
or with_capacity_and_default_hasher
, but I would prefer to see that in the standard library HashMap
first so we can match the bikeshedding. :)
For my current project I re-exported indexmap from my utility crate to get a with_capacity function like so. Would this work in indexmap itself?
pub mod indexmap {
use core::hash::BuildHasherDefault;
pub type IndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<ahash::AHasher>>;
pub use indexmap::map::*;
pub trait IndexMapExt<K, V, S: Default> {
fn with_capacity(capacity: usize) -> Self;
}
impl<K, V, S: Default> IndexMapExt<K, V, S> for indexmap::IndexMap<K, V, S> {
fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, S::default())
}
}
}
It is possible to write it generically like that, but it makes type inference difficult for users, unless they always use an alias binding a specific S
like you're doing. If we allowed any S
in with_capacity
:
// IndexMap<K, V, S> parameters are initially ambiguous
let mut map = IndexMap::with_capacity(10);
// This will drive type inference to K = &'static str and V = i32
map.insert("foo", 42);
// So that's IndexMap<&'static str, i32, S>, but the hasher type is still unknown!