nonempty_containers/nonemptyset.rs
1//! A non-empty set type that guarantees at least one element is present. [NonEmptySet] has an
2//! interface similar to [HashSet] with additional methods to enforce the invariant. Get started
3//! with:
4//!
5//! ```rust, no_run
6//! # use nonempty_containers::{nes, NonEmptySet};
7//! #
8//! let nes = NonEmptySet::new(42, vec![1, 2, 3]);
9//! let singleton = NonEmptySet::singleton(42);
10//! let r#macro = nes![1, 2, 3];
11//! ```
12//!
13//! [NonEmptySet] conforms to [Index], [IntoIterator], [Deref], and many more, so operations are
14//! as [HashSet]-like as possible. They are also usually zero-cost.
15//!
16//! ```rust, no_run
17//! # use nonempty_containers::{nes, NonEmptySet};
18//! #
19//! let nes = nes![42, 1, 2, 3];
20//! assert!(nes.contains(&42));
21//! assert_eq!(nes.len(), 4);
22//! ```
23//!
24//! When the feature `arbitrary` is enabled, [NonEmptySet] implements [arbitrary::Arbitrary]
25//! for generation of randomly populated instances.
26
27use std::collections::hash_set::{Iter, IntoIter};
28use std::collections::HashSet;
29use std::hash::Hash;
30use crate::errors::NonEmptyError;
31
32/// Non-empty set type.
33#[derive(Debug, Eq, PartialEq, Clone)]
34pub struct NonEmptySet<T: Eq + Hash>(HashSet<T>);
35
36impl<T: Eq + Hash> NonEmptySet<T> {
37 /// Creates a new [NonEmptySet], ensuring at least one element is present.
38 pub fn new(head: T, tail: Vec<T>) -> Self {
39 let mut set = HashSet::with_capacity(1 + tail.len());
40 set.insert(head);
41 set.extend(tail);
42 Self(set)
43 }
44
45 /// Creates a new singleton [NonEmptySet]. Semantically equivalent to:
46 /// ```no_run
47 /// # use nonempty_containers::NonEmptySet;
48 /// # let value = 42;
49 /// #
50 /// NonEmptySet::new(value, Vec::new());
51 /// ```
52 pub fn singleton(value: T) -> Self {
53 let mut set = HashSet::new();
54 set.insert(value);
55 Self(set)
56 }
57
58 /// Creates a new [NonEmptySet] from a [HashSet]. Returns an error if the set is empty.
59 pub fn from(set: HashSet<T>) -> Result<Self, NonEmptyError> {
60 match set.is_empty() {
61 true => Err(NonEmptyError::Empty),
62 false => Ok(Self(set)),
63 }
64 }
65
66 /// Creates a new [NonEmptySet] from a [HashSet] without checking the invariant. This is unsafe
67 /// and should only be used by macros in this crate.
68 #[doc(hidden)]
69 pub fn __from_set_unsafe(set: HashSet<T>) -> Self {
70 debug_assert!(!set.is_empty());
71 Self(set)
72 }
73
74 /// Extracts the underlying [HashSet]. This operation is zero-cost.
75 pub fn into_set(self) -> HashSet<T> {
76 self.0
77 }
78
79 /// Returns the size of the set.
80 pub fn len(&self) -> usize {
81 self.0.len()
82 }
83
84 /// A [NonEmptySet] is always non-empty.
85 pub fn is_empty(&self) -> bool {
86 false
87 }
88
89 /// Adds an element to the set. If the element is already present, it is not modified.
90 pub fn insert(&mut self, value: T) -> bool {
91 self.0.insert(value)
92 }
93
94 /// Removes an element from the set. Returns `true` if the element was present.
95 pub fn remove(&mut self, value: &T) -> bool {
96 self.0.remove(value)
97 }
98
99 /// Checks if the set contains a value.
100 pub fn contains(&self, value: &T) -> bool {
101 self.0.contains(value)
102 }
103}
104
105impl<T: Eq + Hash> From<NonEmptySet<T>> for HashSet<T> {
106 fn from(value: NonEmptySet<T>) -> Self {
107 value.into_set()
108 }
109}
110
111impl<T: Eq + Hash> TryFrom<HashSet<T>> for NonEmptySet<T> {
112 type Error = NonEmptyError;
113
114 fn try_from(set: HashSet<T>) -> Result<Self, Self::Error> {
115 NonEmptySet::from(set)
116 }
117}
118
119
120impl<T: Eq + Hash> From<T> for NonEmptySet<T> {
121 fn from(value: T) -> Self {
122 Self::singleton(value)
123 }
124}
125
126impl<'a, T: Eq + Hash> IntoIterator for &'a NonEmptySet<T> {
127 type Item = &'a T;
128 type IntoIter = Iter<'a, T>;
129
130 fn into_iter(self) -> Self::IntoIter {
131 self.0.iter()
132 }
133}
134
135impl<T: Eq + Hash> IntoIterator for NonEmptySet<T> {
136 type Item = T;
137 type IntoIter = IntoIter<T>;
138
139 fn into_iter(self) -> Self::IntoIter {
140 self.0.into_iter()
141 }
142}