Skip to content

Commit 6c3d9da

Browse files
committed
Support hashbrown hash maps and sets with or without use_std
Add an optional `hashbrown` feature that switches the underlying hash map implementation from `std::collections` to `hashbrown` and makes items that depend on hash maps available without `use_std` (although it still requires and implicitly enables `use_alloc`). This addresses the motivating use case of [rust-itertools#605][] (which is the same use case that I am interested in) but does not implement a generic approach as the issue suggests. [rust-itertools#901][] would be a better solution long term and would make these changes obsolete, but the PR appears to be stalled and I wanted a simple solution that allows using APIs like `unique`/`unique_by` with `no_std` now. It also partially addresses [rust-itertools#322][] because enabling this feature does switch to *a* faster hash algorithm, it just doesn't allow to specify which one exactly (currently the default hasher in `hashbrown` is `foldhash`, so this is what's going to be used until/unless changed upstream in the future). [rust-itertools#605]: rust-itertools#605 [rust-itertools#901]: rust-itertools#901 [rust-itertools#322]: rust-itertools#322
1 parent 50d01b4 commit 6c3d9da

File tree

6 files changed

+49
-26
lines changed

6 files changed

+49
-26
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ test = false
2424

2525
[dependencies]
2626
either = { version = "1.0", default-features = false }
27+
hashbrown = { version = "0.15", optional = true, default-features = false, features = [
28+
"alloc",
29+
"default-hasher",
30+
] }
2731

2832
[dev-dependencies]
2933
rand = "0.7"
@@ -36,6 +40,7 @@ quickcheck = { version = "0.9", default-features = false }
3640
default = ["use_std"]
3741
use_std = ["use_alloc", "either/use_std"]
3842
use_alloc = []
43+
hashbrown = ["dep:hashbrown", "use_alloc"]
3944

4045
[profile]
4146
bench = { debug = true }

src/duplicates_impl.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::hash::Hash;
22

33
mod private {
4+
#[cfg(feature = "hashbrown")]
5+
use hashbrown::HashMap;
6+
#[cfg(not(feature = "hashbrown"))]
47
use std::collections::HashMap;
58
use std::fmt;
69
use std::hash::Hash;

src/group_map.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
#![cfg(feature = "use_std")]
1+
#![cfg(any(feature = "use_std", feature = "hashbrown"))]
22

3+
#[cfg(feature = "hashbrown")]
4+
use hashbrown::HashMap;
5+
#[cfg(not(feature = "hashbrown"))]
36
use std::collections::HashMap;
47
use std::hash::Hash;
58
use std::iter::Iterator;

src/grouping_map.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use crate::{
22
adaptors::map::{MapSpecialCase, MapSpecialCaseFn},
33
MinMaxResult,
44
};
5+
#[cfg(feature = "hashbrown")]
6+
use hashbrown::HashMap;
57
use std::cmp::Ordering;
8+
#[cfg(not(feature = "hashbrown"))]
69
use std::collections::HashMap;
710
use std::hash::Hash;
811
use std::iter::Iterator;

src/lib.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@
4747
//! - Enabled by default.
4848
//! - Enables any item that depend on allocations (like `chunk_by`,
4949
//! `kmerge`, `join` and many more).
50+
//! - `hashbrown`
51+
//! - Disabled by default.
52+
//! - Enables `use_alloc` and uses `hashbrown::HashMap` instead of
53+
//! `std::collections::HashMap` to enable some items that depend on hash maps
54+
//! without `use_std`. It is also possible to use this feature together with
55+
//! `use_std` if you prefer `hashbrown::HashMap` (for example, because of the
56+
//! different default hasher).
5057
//!
5158
//! ## Rust Version
5259
//!
@@ -64,15 +71,15 @@ use alloc::{collections::VecDeque, string::String, vec::Vec};
6471
pub use either::Either;
6572

6673
use core::borrow::Borrow;
74+
#[cfg(feature = "hashbrown")]
75+
use hashbrown::{HashMap, HashSet};
6776
use std::cmp::Ordering;
68-
#[cfg(feature = "use_std")]
69-
use std::collections::HashMap;
70-
#[cfg(feature = "use_std")]
71-
use std::collections::HashSet;
77+
#[cfg(all(feature = "use_std", not(feature = "hashbrown")))]
78+
use std::collections::{HashMap, HashSet};
7279
use std::fmt;
7380
#[cfg(feature = "use_alloc")]
7481
use std::fmt::Write;
75-
#[cfg(feature = "use_std")]
82+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
7683
use std::hash::Hash;
7784
use std::iter::{once, IntoIterator};
7885
#[cfg(feature = "use_alloc")]
@@ -102,7 +109,7 @@ pub mod structs {
102109
#[cfg(feature = "use_alloc")]
103110
pub use crate::combinations_with_replacement::CombinationsWithReplacement;
104111
pub use crate::cons_tuples_impl::ConsTuples;
105-
#[cfg(feature = "use_std")]
112+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
106113
pub use crate::duplicates_impl::{Duplicates, DuplicatesBy};
107114
pub use crate::exactly_one_err::ExactlyOneError;
108115
pub use crate::flatten_ok::FlattenOk;
@@ -112,7 +119,7 @@ pub mod structs {
112119
pub use crate::groupbylazy::GroupBy;
113120
#[cfg(feature = "use_alloc")]
114121
pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks};
115-
#[cfg(feature = "use_std")]
122+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
116123
pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
117124
pub use crate::intersperse::{Intersperse, IntersperseWith};
118125
#[cfg(feature = "use_alloc")]
@@ -140,7 +147,7 @@ pub mod structs {
140147
#[cfg(feature = "use_alloc")]
141148
pub use crate::tee::Tee;
142149
pub use crate::tuple_impl::{CircularTupleWindows, TupleBuffer, TupleWindows, Tuples};
143-
#[cfg(feature = "use_std")]
150+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
144151
pub use crate::unique_impl::{Unique, UniqueBy};
145152
pub use crate::with_position::WithPosition;
146153
pub use crate::zip_eq_impl::ZipEq;
@@ -187,18 +194,18 @@ mod combinations_with_replacement;
187194
mod concat_impl;
188195
mod cons_tuples_impl;
189196
mod diff;
190-
#[cfg(feature = "use_std")]
197+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
191198
mod duplicates_impl;
192199
mod exactly_one_err;
193200
#[cfg(feature = "use_alloc")]
194201
mod extrema_set;
195202
mod flatten_ok;
196203
mod format;
197-
#[cfg(feature = "use_alloc")]
204+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
198205
mod group_map;
199206
#[cfg(feature = "use_alloc")]
200207
mod groupbylazy;
201-
#[cfg(feature = "use_std")]
208+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
202209
mod grouping_map;
203210
mod intersperse;
204211
mod iter_index;
@@ -233,7 +240,7 @@ mod take_while_inclusive;
233240
#[cfg(feature = "use_alloc")]
234241
mod tee;
235242
mod tuple_impl;
236-
#[cfg(feature = "use_std")]
243+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
237244
mod unique_impl;
238245
mod unziptuple;
239246
mod with_position;
@@ -1415,7 +1422,7 @@ pub trait Itertools: Iterator {
14151422
/// itertools::assert_equal(data.into_iter().duplicates(),
14161423
/// vec![20, 10]);
14171424
/// ```
1418-
#[cfg(feature = "use_std")]
1425+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
14191426
fn duplicates(self) -> Duplicates<Self>
14201427
where
14211428
Self: Sized,
@@ -1441,7 +1448,7 @@ pub trait Itertools: Iterator {
14411448
/// itertools::assert_equal(data.into_iter().duplicates_by(|s| s.len()),
14421449
/// vec!["aa", "c"]);
14431450
/// ```
1444-
#[cfg(feature = "use_std")]
1451+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
14451452
fn duplicates_by<V, F>(self, f: F) -> DuplicatesBy<Self, V, F>
14461453
where
14471454
Self: Sized,
@@ -1469,7 +1476,7 @@ pub trait Itertools: Iterator {
14691476
/// itertools::assert_equal(data.into_iter().unique(),
14701477
/// vec![10, 20, 30, 40, 50]);
14711478
/// ```
1472-
#[cfg(feature = "use_std")]
1479+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
14731480
fn unique(self) -> Unique<Self>
14741481
where
14751482
Self: Sized,
@@ -1496,7 +1503,7 @@ pub trait Itertools: Iterator {
14961503
/// itertools::assert_equal(data.into_iter().unique_by(|s| s.len()),
14971504
/// vec!["a", "bb", "ccc"]);
14981505
/// ```
1499-
#[cfg(feature = "use_std")]
1506+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
15001507
fn unique_by<V, F>(self, f: F) -> UniqueBy<Self, V, F>
15011508
where
15021509
Self: Sized,
@@ -2309,7 +2316,7 @@ pub trait Itertools: Iterator {
23092316
/// let data: Option<usize> = None;
23102317
/// assert!(data.into_iter().all_unique());
23112318
/// ```
2312-
#[cfg(feature = "use_std")]
2319+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
23132320
fn all_unique(&mut self) -> bool
23142321
where
23152322
Self: Sized,
@@ -3744,7 +3751,7 @@ pub trait Itertools: Iterator {
37443751
/// assert_eq!(lookup[&2], vec![12, 42]);
37453752
/// assert_eq!(lookup[&3], vec![13, 33]);
37463753
/// ```
3747-
#[cfg(feature = "use_std")]
3754+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
37483755
fn into_group_map<K, V>(self) -> HashMap<K, Vec<V>>
37493756
where
37503757
Self: Iterator<Item = (K, V)> + Sized,
@@ -3780,7 +3787,7 @@ pub trait Itertools: Iterator {
37803787
/// 30,
37813788
/// );
37823789
/// ```
3783-
#[cfg(feature = "use_std")]
3790+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
37843791
fn into_group_map_by<K, V, F>(self, f: F) -> HashMap<K, Vec<V>>
37853792
where
37863793
Self: Iterator<Item = V> + Sized,
@@ -3799,7 +3806,7 @@ pub trait Itertools: Iterator {
37993806
///
38003807
/// See [`GroupingMap`] for more informations
38013808
/// on what operations are available.
3802-
#[cfg(feature = "use_std")]
3809+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
38033810
fn into_grouping_map<K, V>(self) -> GroupingMap<Self>
38043811
where
38053812
Self: Iterator<Item = (K, V)> + Sized,
@@ -3816,7 +3823,7 @@ pub trait Itertools: Iterator {
38163823
///
38173824
/// See [`GroupingMap`] for more informations
38183825
/// on what operations are available.
3819-
#[cfg(feature = "use_std")]
3826+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
38203827
fn into_grouping_map_by<K, V, F>(self, key_mapper: F) -> GroupingMapBy<Self, F>
38213828
where
38223829
Self: Iterator<Item = V> + Sized,
@@ -4527,7 +4534,7 @@ pub trait Itertools: Iterator {
45274534
/// assert_eq!(counts[&5], 1);
45284535
/// assert_eq!(counts.get(&0), None);
45294536
/// ```
4530-
#[cfg(feature = "use_std")]
4537+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
45314538
fn counts(self) -> HashMap<Self::Item, usize>
45324539
where
45334540
Self: Sized,
@@ -4571,7 +4578,7 @@ pub trait Itertools: Iterator {
45714578
/// assert_eq!(first_name_frequency["James"], 4);
45724579
/// assert_eq!(first_name_frequency.contains_key("Asha"), false);
45734580
/// ```
4574-
#[cfg(feature = "use_std")]
4581+
#[cfg(any(feature = "use_std", feature = "hashbrown"))]
45754582
fn counts_by<K, F>(self, f: F) -> HashMap<K, usize>
45764583
where
45774584
Self: Sized,

src/unique_impl.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use std::collections::hash_map::Entry;
2-
use std::collections::HashMap;
1+
#[cfg(feature = "hashbrown")]
2+
use hashbrown::{hash_map::Entry, HashMap};
3+
#[cfg(not(feature = "hashbrown"))]
4+
use std::collections::{hash_map::Entry, HashMap};
35
use std::fmt;
46
use std::hash::Hash;
57
use std::iter::FusedIterator;

0 commit comments

Comments
 (0)