Is there a type that preserves insertion order (think Vec
) but only tracks unique values (think HashSet
)? I want to avoid using Vec
because I would first need to check if the value exists in it before insertion.
Asked
Active
Viewed 8,634 times
16

Shepmaster
- 388,571
- 95
- 1,107
- 1,366

tshepang
- 12,111
- 21
- 91
- 136
-
1Did you see [linked-hash-map](https://crates.io/crates/linked-hash-map) crate? Maybe you can create a wrap with `V = ()`. – malbarbo May 31 '16 at 15:58
-
1Essentially a duplicate of http://stackoverflow.com/q/30243100/155423 as sets are just maps without a value. – Shepmaster May 31 '16 at 15:58
-
If the only change you need is adding elements and the elements are copyable, you could simply use both a set and a vector, using the set for the duplicate checks and the vector to store the elements in-order. – CodesInChaos Jun 01 '16 at 08:27
-
@CodesInChaos can you show what the code would look like – tshepang Jun 01 '16 at 22:59
-
Which operation will be more used? Insert or remove? – malbarbo Jun 01 '16 at 23:14
-
About equal @malbarbo – tshepang Jun 01 '16 at 23:16
-
@Tshepang The Rust equivalent of [this C# code](https://gist.github.com/anonymous/cf8c67145f7d508aae123d41de59fa12). For Copy types this should have a direct equivalent in Rust. But since you clarified that you don't just need to add elements but also remove them, this approach won't work for you. – CodesInChaos Jun 02 '16 at 06:53
4 Answers
12
linked_hash_set
crate is now available. It's based on the
linked-hash-map
crate mirroring the std HashSet
API as closely as possible.
extern crate linked_hash_set;
use linked_hash_set::LinkedHashSet;
let mut set = LinkedHashSet::new();
set.insert(234);
set.insert(123);
set.insert(345);
set.insert(123);
assert_eq!(set.into_iter().collect::<Vec<_>>(), vec![234, 345, 123]);

Alex Butler
- 306
- 3
- 6
-
I was disappointed to see it didn't implement retain() and I can't derive Deserialize for it. Just FYI. – Andrew Mackenzie Apr 15 '22 at 15:56
-
`linked-hash-map` does not implement `retain` which would be the first step for that. For serialization the optional "serde" dependency/feature is available. – Alex Butler Jun 11 '22 at 10:50
-
It is worth noting that when removal is not a frequently expected operation, [IndexSet](https://docs.rs/indexmap/latest/indexmap/set/struct.IndexSet.html) is a more appropriate choice giving better performance. – nirvana-msu Jun 24 '22 at 16:24
5
Updating answers since linked-hash-map
is in maintenance mode (see github commit)
The most popular github crates in September 2021:
- indexmap which provide direct implementation of
IndexSet
. - array_tool which provide helper to build a vector set data structure with few lines of code.
4
The linked-hash-map
crate provides a hash map that holds key-value insertion order. We can create a set wrapper for this hash map using ()
as values (std::collections::HashSet
is implemented this way):
extern crate linked_hash_map;
use linked_hash_map::*;
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash};
use std::borrow::Borrow;
fn main() {
let mut s = LinkedHashSet::new();
s.insert(5);
s.insert(3);
s.insert(7);
s.insert(1);
assert_eq!(vec![5, 3, 7, 1], s.iter().cloned().collect::<Vec<_>>());
s.remove(&7);
assert_eq!(vec![5, 3, 1], s.iter().cloned().collect::<Vec<_>>());
s.remove(&5);
assert_eq!(vec![3, 1], s.iter().cloned().collect::<Vec<_>>());
}
pub struct LinkedHashSet<K, S = RandomState>(LinkedHashMap<K, (), S>);
impl<K: Hash + Eq> LinkedHashSet<K> {
pub fn new() -> Self {
LinkedHashSet(LinkedHashMap::new())
}
}
impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
pub fn insert(&mut self, k: K) -> Option<()> {
self.0.insert(k, ())
}
pub fn contains<Q: ?Sized>(&self, k: &Q) -> bool
where K: Borrow<Q>,
Q: Eq + Hash
{
self.0.contains_key(k)
}
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<()>
where K: Borrow<Q>,
Q: Eq + Hash
{
self.0.remove(k)
}
pub fn iter(&self) -> Keys<K, ()> {
self.0.keys()
}
}
You can implement other methods. See LinkedHashMap
docs.

malbarbo
- 10,717
- 1
- 42
- 57
0
RiteLinked provides more up to date versions of LinkedHashMap & LinkedHashSet in Rust. You can easily use it on std or no_std environment. You can use it instead of linked-hash-map and linked_hash_set.

PsiACE
- 106
- 4