satellite_collections/generic/fixed_set.rs
1//! Fixed-capacity set implementation for allocation-free environments.
2//!
3//! This module provides [`FixedSet`], a set-like collection for storing unique elements
4//! with compile-time capacity bounds, and [`FixedCapacitySet`], a trait for generic
5//! set operations.
6
7use crate::generic::push_pop::{PushPopCollection, PushPopError};
8use std::fmt::Debug;
9
10/// Error type for [`FixedSet`] operations.
11#[derive(Debug, PartialEq, Eq)]
12pub enum FixedSetError {
13 /// The set has reached its maximum capacity.
14 Full,
15 /// The item already exists in the set.
16 Duplicate,
17}
18
19impl From<FixedSetError> for u32 {
20 fn from(e: FixedSetError) -> Self {
21 match e {
22 FixedSetError::Full => 1,
23 FixedSetError::Duplicate => 2,
24 }
25 }
26}
27
28impl std::fmt::Display for FixedSetError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 write!(f, "{:?}", self)
31 }
32}
33
34/// Generic trait for fixed-capacity set operations.
35///
36/// This trait provides a common interface for set-like collections with compile-time
37/// capacity bounds. It's implemented by both [`FixedSet`] and types generated by
38/// the [`declare_fixed_set!`] macro.
39///
40/// # Examples
41///
42/// ```rust
43/// use satellite_bitcoin::generic::fixed_set::{FixedSet, FixedCapacitySet};
44///
45/// fn work_with_set<S: FixedCapacitySet<Item = i32>>(set: &mut S) {
46/// set.insert(42).unwrap();
47/// set.insert(100).unwrap();
48/// assert_eq!(set.len(), 2);
49/// assert!(set.contains(&42));
50/// }
51///
52/// let mut set = FixedSet::<i32, 10>::new();
53/// work_with_set(&mut set);
54/// ```
55///
56/// [`declare_fixed_set!`]: crate::declare_fixed_set
57pub trait FixedCapacitySet: Default + Clone + Debug {
58 /// The element type stored in the set.
59 type Item: Copy + PartialEq + Default;
60
61 /// Maximum number of elements the set can hold.
62 fn capacity(&self) -> usize;
63
64 /// Current number of stored elements.
65 fn len(&self) -> usize;
66
67 /// Check whether the set currently contains `item`.
68 fn contains<Q>(&self, item: &Q) -> bool
69 where
70 Self::Item: PartialEq<Q>;
71
72 /// Find the first item equal to `item` in the set.
73 fn find<Q>(&self, item: &Q) -> Option<&Self::Item>
74 where
75 Self::Item: PartialEq<Q>;
76
77 /// Find the first item equal to `item` in the set and return it.
78 fn find_mut<Q>(&mut self, item: &Q) -> Option<&mut Self::Item>
79 where
80 Self::Item: PartialEq<Q>;
81
82 /// Attempt to insert `item`.
83 /// Returns `Ok(())` on success or an appropriate [`FixedSetError`].
84 fn insert(&mut self, item: Self::Item) -> Result<(), FixedSetError>;
85
86 /// Inserts `item` into the set if it is not already present.
87 /// If it is present, modifies it using the provided function.
88 /// Returns `Ok(())` on success or an appropriate [`FixedSetError`].
89 fn insert_or_modify<E, F>(&mut self, item: Self::Item, modify: F) -> Result<(), E>
90 where
91 F: FnMut(&mut Self::Item) -> Result<(), E>,
92 E: From<FixedSetError>;
93
94 /// Remove `item` if present, returning it.
95 fn remove<Q>(&mut self, item: &Q) -> Option<Self::Item>
96 where
97 Self::Item: PartialEq<Q>;
98
99 /// Access the underlying slice of active items.
100 fn as_slice(&self) -> &[Self::Item];
101
102 /// Returns the elements of the set as a mutable slice.
103 fn as_mut_slice(&mut self) -> &mut [Self::Item];
104
105 /// Iterate over the set.
106 fn iter(&self) -> impl Iterator<Item = &Self::Item> {
107 self.as_slice().iter()
108 }
109
110 /// Iterate over the set mutably.
111 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> + '_ {
112 self.as_mut_slice().iter_mut()
113 }
114}
115
116/// A fixed-capacity set for storing unique elements.
117///
118/// `FixedSet` maintains a collection of unique elements with compile-time capacity bounds.
119/// It provides efficient insertion, removal, and lookup operations while maintaining
120/// element uniqueness.
121///
122/// # Type Parameters
123///
124/// * `T` - The element type. Must implement `Default + Copy + PartialEq`.
125/// * `SIZE` - The maximum number of elements the set can hold (compile-time constant).
126///
127/// # Examples
128///
129/// ```rust
130/// use satellite_bitcoin::generic::fixed_set::FixedSet;
131///
132/// let mut set: FixedSet<u32, 8> = FixedSet::new();
133///
134/// set.insert(3).unwrap();
135/// set.insert(5).unwrap();
136/// assert!(set.contains(&3));
137/// assert_eq!(set.len(), 2);
138///
139/// // Duplicate insertion fails
140/// assert!(set.insert(3).is_err());
141///
142/// // Remove elements
143/// assert_eq!(set.remove(&3), Some(3));
144/// assert_eq!(set.len(), 1);
145/// ```
146///
147/// # Memory Layout
148///
149/// The set stores elements in a contiguous array followed by a length field.
150/// Element order is not preserved across insertions and removals due to the
151/// swap-remove optimization used for O(1) removal.
152///
153/// # Bytemuck Compatibility
154///
155/// `FixedSet` implements `Pod` and `Zeroable` when `T` implements these traits,
156/// making it suitable for zero-copy serialization in on-chain programs.
157#[derive(Debug, Clone, Copy)]
158#[repr(C)]
159pub struct FixedSet<T, const SIZE: usize> {
160 items: [T; SIZE],
161 len: usize,
162 _padding: [u8; 8],
163}
164
165impl<T: Default + Copy + PartialEq, const SIZE: usize> Default for FixedSet<T, SIZE> {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171impl<T: Default + Copy + PartialEq, const SIZE: usize> FixedSet<T, SIZE> {
172 /// Creates an empty `FixedSet`.
173 ///
174 /// # Examples
175 ///
176 /// ```rust
177 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
178 ///
179 /// let set: FixedSet<u32, 10> = FixedSet::new();
180 /// assert!(set.is_empty());
181 /// assert_eq!(set.len(), 0);
182 /// ```
183 pub fn new() -> Self {
184 Self {
185 items: [T::default(); SIZE],
186 len: 0,
187 _padding: [0; 8],
188 }
189 }
190
191 /// Returns the number of elements currently stored in the set.
192 ///
193 /// # Examples
194 ///
195 /// ```rust
196 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
197 ///
198 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
199 /// assert_eq!(set.len(), 0);
200 ///
201 /// set.insert(42).unwrap();
202 /// assert_eq!(set.len(), 1);
203 /// ```
204 pub fn len(&self) -> usize {
205 self.len
206 }
207
208 /// Returns `true` if the set contains no elements.
209 ///
210 /// # Examples
211 ///
212 /// ```rust
213 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
214 ///
215 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
216 /// assert!(set.is_empty());
217 ///
218 /// set.insert(42).unwrap();
219 /// assert!(!set.is_empty());
220 /// ```
221 pub fn is_empty(&self) -> bool {
222 self.len == 0
223 }
224
225 /// Returns an iterator over all items currently in the set.
226 ///
227 /// # Examples
228 ///
229 /// ```rust
230 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
231 ///
232 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
233 /// set.insert(1).unwrap();
234 /// set.insert(2).unwrap();
235 ///
236 /// let collected: Vec<_> = set.iter().copied().collect();
237 /// assert_eq!(collected.len(), 2);
238 /// assert!(collected.contains(&1));
239 /// assert!(collected.contains(&2));
240 /// ```
241 pub fn iter(&self) -> impl Iterator<Item = &T> + '_ {
242 self.items[..self.len].iter()
243 }
244
245 /// Returns a mutable iterator over all items currently in the set.
246 ///
247 /// # Examples
248 ///
249 /// ```rust
250 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
251 ///
252 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
253 /// set.insert(1).unwrap();
254 /// set.insert(2).unwrap();
255 ///
256 /// for item in set.iter_mut() {
257 /// *item *= 10;
258 /// }
259 ///
260 /// assert!(set.contains(&10));
261 /// assert!(set.contains(&20));
262 /// ```
263 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> + '_ {
264 self.items[..self.len].iter_mut()
265 }
266
267 /// Inserts `item` into the set if it is not already present.
268 /// If it is present, modifies it using the provided function.
269 ///
270 /// This is useful for implementing "upsert" semantics where you want to
271 /// insert a new item or update an existing one.
272 ///
273 /// # Examples
274 ///
275 /// ```rust
276 /// use satellite_bitcoin::generic::fixed_set::{FixedSet, FixedSetError};
277 ///
278 /// #[derive(Clone, Copy, Debug, Default)]
279 /// struct Counter { id: u32, count: u32 }
280 ///
281 /// impl PartialEq for Counter {
282 /// fn eq(&self, other: &Self) -> bool {
283 /// self.id == other.id // Only compare by ID
284 /// }
285 /// }
286 ///
287 /// impl Eq for Counter {}
288 ///
289 /// #[derive(Debug)]
290 /// enum MyError {
291 /// SetError(FixedSetError),
292 /// }
293 ///
294 /// impl From<FixedSetError> for MyError {
295 /// fn from(e: FixedSetError) -> Self {
296 /// MyError::SetError(e)
297 /// }
298 /// }
299 ///
300 /// let mut set: FixedSet<Counter, 10> = FixedSet::new();
301 ///
302 /// // Insert new item
303 /// set.insert_or_modify(
304 /// Counter { id: 1, count: 1 },
305 /// |_| Ok::<(), MyError>(())
306 /// ).unwrap();
307 ///
308 /// // Update existing item
309 /// set.insert_or_modify(
310 /// Counter { id: 1, count: 0 }, // count doesn't matter for lookup
311 /// |existing| { existing.count += 1; Ok::<(), MyError>(()) }
312 /// ).unwrap();
313 ///
314 /// assert_eq!(set.find(&Counter { id: 1, count: 0 }).unwrap().count, 2);
315 /// ```
316 pub fn insert_or_modify<E, F>(&mut self, item: T, mut modify: F) -> Result<(), E>
317 where
318 F: FnMut(&mut T) -> Result<(), E>,
319 E: From<FixedSetError>,
320 {
321 if self.contains(&item) {
322 let pos = self.iter().position(|i| *i == item).unwrap();
323 modify(&mut self.items[pos])?;
324 } else {
325 self.insert(item).map_err(E::from)?;
326 }
327
328 Ok(())
329 }
330
331 /// Returns `true` if the set already contains `item`.
332 ///
333 /// # Examples
334 ///
335 /// ```rust
336 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
337 ///
338 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
339 /// assert!(!set.contains(&42));
340 ///
341 /// set.insert(42).unwrap();
342 /// assert!(set.contains(&42));
343 /// ```
344 pub fn contains<Q>(&self, item: &Q) -> bool
345 where
346 T: PartialEq<Q>,
347 {
348 self.iter().any(|i| i == item)
349 }
350
351 /// Returns the first item equal to `item` in the set.
352 ///
353 /// This is useful when you need to access the actual stored item that matches
354 /// your query, especially when using custom equality implementations.
355 ///
356 /// # Examples
357 ///
358 /// ```rust
359 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
360 ///
361 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
362 /// set.insert(42).unwrap();
363 ///
364 /// assert_eq!(set.find(&42), Some(&42));
365 /// assert_eq!(set.find(&99), None);
366 /// ```
367 pub fn find<Q>(&self, item: &Q) -> Option<&T>
368 where
369 T: PartialEq<Q>,
370 {
371 self.iter().find(|i| *i == item)
372 }
373
374 /// Returns the first item equal to `item` in the set (mutable reference).
375 ///
376 /// # Examples
377 ///
378 /// ```rust
379 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
380 ///
381 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
382 /// set.insert(42).unwrap();
383 ///
384 /// if let Some(item) = set.find_mut(&42) {
385 /// *item = 100;
386 /// }
387 ///
388 /// assert!(set.contains(&100));
389 /// assert!(!set.contains(&42));
390 /// ```
391 pub fn find_mut<Q>(&mut self, item: &Q) -> Option<&mut T>
392 where
393 T: PartialEq<Q>,
394 {
395 self.iter_mut().find(|i| *i == item)
396 }
397
398 /// Attempts to insert `item` into the set.
399 ///
400 /// # Errors
401 ///
402 /// * Returns `Err(FixedSetError::Duplicate)` if the item already exists in the set.
403 /// * Returns `Err(FixedSetError::Full)` if the set is at full capacity.
404 ///
405 /// # Examples
406 ///
407 /// ```rust
408 /// use satellite_bitcoin::generic::fixed_set::{FixedSet, FixedSetError};
409 ///
410 /// let mut set: FixedSet<u32, 2> = FixedSet::new();
411 /// assert!(set.insert(1).is_ok());
412 /// assert!(set.insert(2).is_ok());
413 ///
414 /// // Duplicate
415 /// assert_eq!(set.insert(1), Err(FixedSetError::Duplicate));
416 /// // Full
417 /// assert_eq!(set.insert(3), Err(FixedSetError::Full));
418 /// ```
419 pub fn insert(&mut self, item: T) -> Result<(), FixedSetError> {
420 if self.contains(&item) {
421 return Err(FixedSetError::Duplicate);
422 }
423 if self.len >= SIZE {
424 return Err(FixedSetError::Full);
425 }
426 self.items[self.len] = item;
427 self.len += 1;
428 Ok(())
429 }
430
431 /// Removes an item equal to `item` from the set and returns it.
432 ///
433 /// Uses swap-remove for O(1) performance, which means element order is not preserved.
434 ///
435 /// # Examples
436 ///
437 /// ```rust
438 /// use satellite_bitcoin::generic::fixed_set::FixedSet;
439 ///
440 /// let mut set: FixedSet<u32, 10> = FixedSet::new();
441 /// set.insert(42).unwrap();
442 ///
443 /// assert_eq!(set.remove(&42), Some(42));
444 /// assert_eq!(set.remove(&42), None);
445 /// assert_eq!(set.len(), 0);
446 /// ```
447 pub fn remove<Q>(&mut self, item: &Q) -> Option<T>
448 where
449 T: PartialEq<Q>,
450 {
451 // Search for the item index without creating an iterator that borrows `self`.
452 let pos_opt = (0..self.len).find(|&i| self.items[i] == *item);
453 if let Some(pos) = pos_opt {
454 // Swap-remove: replace the removed element with the last one to keep O(1) removal.
455 let removed = self.items[pos];
456 self.len -= 1;
457 if pos != self.len {
458 self.items[pos] = self.items[self.len];
459 }
460 Some(removed)
461 } else {
462 None
463 }
464 }
465
466 /// Removes and returns an arbitrary element from the set (the last one).
467 pub fn pop(&mut self) -> Option<T> {
468 if self.len > 0 {
469 self.len -= 1;
470 Some(self.items[self.len])
471 } else {
472 None
473 }
474 }
475
476 /// Returns the elements of the set as a slice.
477 pub fn as_slice(&self) -> &[T] {
478 &self.items[..self.len]
479 }
480
481 /// Returns the elements of the set as a mutable slice.
482 pub fn as_mut_slice(&mut self) -> &mut [T] {
483 &mut self.items[..self.len]
484 }
485
486 /// Creates a [`FixedSet`] from any iterator, returning an error if the
487 /// number of unique elements exceeds the set's capacity.
488 ///
489 /// Duplicate elements yielded by the iterator are ignored (because a set
490 /// cannot contain the same value twice). However, if inserting additional
491 /// *unique* elements would exceed the compile-time capacity `SIZE`, the
492 /// construction fails with [`FixedSetError::Full`].
493 pub fn try_from_iter<I>(iter: I) -> Result<Self, FixedSetError>
494 where
495 I: IntoIterator<Item = T>,
496 {
497 let mut set = Self::new();
498 for item in iter {
499 match set.insert(item) {
500 Ok(()) | Err(FixedSetError::Duplicate) => {}
501 Err(FixedSetError::Full) => return Err(FixedSetError::Full),
502 }
503 }
504 Ok(set)
505 }
506
507 /// Returns a reference to the **first** element in the set (if any).
508 ///
509 /// This is a convenience helper useful when the set is known to hold at
510 /// most one element (e.g. singleārune outputs). It avoids having to call
511 /// `as_slice().first()` at every call-site.
512 #[inline]
513 pub fn get(&self) -> Option<&T> {
514 self.as_slice().first()
515 }
516}
517
518impl<T: Default + Copy + PartialEq, const SIZE: usize> PushPopCollection<T> for FixedSet<T, SIZE> {
519 fn push(&mut self, item: T) -> Result<(), PushPopError> {
520 match self.insert(item) {
521 Ok(()) => Ok(()),
522 Err(FixedSetError::Full) => Err(PushPopError::Full),
523 // Treat duplicate insertions as a no-op success for the generic API.
524 Err(FixedSetError::Duplicate) => Ok(()),
525 }
526 }
527
528 fn pop(&mut self) -> Option<T> {
529 self.pop()
530 }
531
532 fn as_slice(&self) -> &[T] {
533 self.as_slice()
534 }
535
536 fn len(&self) -> usize {
537 self.len()
538 }
539
540 fn max_size(&self) -> usize {
541 SIZE
542 }
543}
544
545impl<T: Default + Copy + PartialEq + Debug, const SIZE: usize> FixedCapacitySet
546 for FixedSet<T, SIZE>
547{
548 type Item = T;
549
550 fn capacity(&self) -> usize {
551 SIZE
552 }
553
554 fn len(&self) -> usize {
555 self.len()
556 }
557
558 fn contains<Q>(&self, item: &Q) -> bool
559 where
560 Self::Item: PartialEq<Q>,
561 {
562 self.contains(item)
563 }
564
565 fn find<Q>(&self, item: &Q) -> Option<&Self::Item>
566 where
567 Self::Item: PartialEq<Q>,
568 {
569 self.find(item)
570 }
571
572 fn find_mut<Q>(&mut self, item: &Q) -> Option<&mut Self::Item>
573 where
574 Self::Item: PartialEq<Q>,
575 {
576 self.find_mut(item)
577 }
578
579 fn insert(&mut self, item: Self::Item) -> Result<(), FixedSetError> {
580 self.insert(item)
581 }
582
583 fn insert_or_modify<E, F>(&mut self, item: Self::Item, modify: F) -> Result<(), E>
584 where
585 F: FnMut(&mut Self::Item) -> Result<(), E>,
586 E: From<FixedSetError>,
587 {
588 self.insert_or_modify(item, modify)
589 }
590
591 fn remove<Q>(&mut self, item: &Q) -> Option<Self::Item>
592 where
593 Self::Item: PartialEq<Q>,
594 {
595 self.remove(item)
596 }
597
598 fn as_slice(&self) -> &[Self::Item] {
599 self.as_slice()
600 }
601
602 fn as_mut_slice(&mut self) -> &mut [Self::Item] {
603 self.as_mut_slice()
604 }
605}
606
607// SAFETY: FixedSet is `Pod` if its items are `Pod` and it is `repr(C)` with
608// no padding between fields that contain invalid bytes. The `items` array is
609// `Pod` when `T` is `Pod`. Both `usize` and arrays of `Pod` types are `Pod`,
610// so the struct as a whole is `Pod`.
611unsafe impl<T: bytemuck::Pod, const SIZE: usize> bytemuck::Pod for FixedSet<T, SIZE> {}
612
613// SAFETY: A `Zeroable` type must have all-bits-zero represent a valid value.
614// Because `FixedSet::default()` sets `len` to 0 and the `items` array to all
615// `T::default()`, zeroing the entire struct is sound when `T` itself is
616// `Zeroable` (all-bits-zero is a valid representation for `T`).
617unsafe impl<T: bytemuck::Zeroable, const SIZE: usize> bytemuck::Zeroable for FixedSet<T, SIZE> {}
618
619// Conversions to/from FixedList
620impl<T: Default + Copy + PartialEq, const SIZE: usize>
621 From<crate::generic::fixed_list::FixedList<T, SIZE>> for FixedSet<T, SIZE>
622{
623 fn from(list: crate::generic::fixed_list::FixedList<T, SIZE>) -> Self {
624 let mut set = Self::new();
625 for item in list.as_slice().iter().copied() {
626 let _ = set.insert(item);
627 }
628 set
629 }
630}
631
632// Try to build a FixedSet from a slice. Deduplicates; errors on overflow of unique elements.
633impl<T: Default + Copy + PartialEq, const SIZE: usize> core::convert::TryFrom<&[T]>
634 for FixedSet<T, SIZE>
635{
636 type Error = FixedSetError;
637
638 fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
639 let mut set = Self::new();
640 for &item in slice.iter() {
641 match set.insert(item) {
642 Ok(()) | Err(FixedSetError::Duplicate) => {}
643 Err(FixedSetError::Full) => return Err(FixedSetError::Full),
644 }
645 }
646 Ok(set)
647 }
648}
649
650#[cfg(test)]
651mod from_slice_tests {
652 use super::*;
653
654 #[test]
655 fn set_try_from_slice_dedups_and_caps() {
656 let data = [1u32, 2, 2, 3, 4];
657 let set = <FixedSet<u32, 3>>::try_from(&data[..]).unwrap();
658 assert_eq!(set.len(), 3);
659 assert!(set.contains(&1) && set.contains(&2) && set.contains(&3));
660 }
661
662 #[test]
663 fn set_try_from_slice_errors_on_overflow() {
664 let data = [1u32, 2, 3, 4];
665 let res = <FixedSet<u32, 3>>::try_from(&data[..]);
666 assert!(matches!(res, Err(FixedSetError::Full)));
667 }
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673
674 #[test]
675 fn test_default_is_empty() {
676 let set = FixedSet::<u32, 4>::default();
677 assert_eq!(set.len(), 0);
678 assert!(set.is_empty());
679 }
680
681 #[test]
682 fn test_insert_and_len() {
683 let mut set = FixedSet::<u32, 3>::new();
684 assert!(set.insert(10).is_ok());
685 assert!(set.insert(20).is_ok());
686 assert_eq!(set.len(), 2);
687 assert!(!set.is_empty());
688 assert!(set.contains(&10));
689 assert!(set.contains(&20));
690 }
691
692 #[test]
693 fn test_duplicate_insertion() {
694 let mut set = FixedSet::<u32, 2>::new();
695 assert!(set.insert(1).is_ok());
696 assert_eq!(set.insert(1), Err(FixedSetError::Duplicate));
697 assert_eq!(set.len(), 1);
698 }
699
700 #[test]
701 fn test_insert_past_capacity() {
702 let mut set = FixedSet::<u32, 2>::new();
703 assert!(set.insert(1).is_ok());
704 assert!(set.insert(2).is_ok());
705 assert_eq!(set.insert(3), Err(FixedSetError::Full));
706 assert_eq!(set.len(), 2);
707 }
708
709 #[test]
710 fn test_remove() {
711 let mut set = FixedSet::<u32, 3>::new();
712 set.insert(5).unwrap();
713 set.insert(10).unwrap();
714 assert_eq!(set.remove(&5), Some(5));
715 assert!(!set.contains(&5));
716 assert_eq!(set.len(), 1);
717 }
718
719 #[test]
720 fn test_pop() {
721 let mut set = FixedSet::<u32, 2>::new();
722 assert_eq!(set.pop(), None);
723
724 set.insert(100).unwrap();
725 set.insert(200).unwrap();
726 let first = set.pop();
727 let second = set.pop();
728 assert!(matches!(
729 (first, second),
730 (Some(200), Some(100)) | (Some(100), Some(200))
731 ));
732 assert!(set.is_empty());
733 }
734
735 #[test]
736 fn test_push_pop_collection_trait() {
737 let mut set = FixedSet::<u8, 2>::new();
738 PushPopCollection::push(&mut set, 1).unwrap();
739 PushPopCollection::push(&mut set, 2).unwrap();
740 PushPopCollection::push(&mut set, 2).unwrap(); // duplicate ignored
741 assert_eq!(PushPopCollection::len(&set), 2);
742 let slice = PushPopCollection::as_slice(&set);
743 assert!(slice.contains(&1) && slice.contains(&2));
744 PushPopCollection::pop(&mut set);
745 PushPopCollection::pop(&mut set);
746 assert_eq!(PushPopCollection::pop(&mut set), None);
747 }
748
749 #[test]
750 fn test_try_from_iter() {
751 // Successful construction within capacity
752 let data = vec![1, 2, 3];
753 let set = FixedSet::<u32, 3>::try_from_iter(data).unwrap();
754 assert_eq!(set.len(), 3);
755 assert!(set.contains(&1) && set.contains(&2) && set.contains(&3));
756
757 // Construction that exceeds capacity should error
758 let too_many = vec![1, 2, 3, 4];
759 let res = FixedSet::<u32, 3>::try_from_iter(too_many);
760 assert!(matches!(res, Err(FixedSetError::Full)));
761 }
762
763 #[test]
764 fn test_generic_search_methods() {
765 // Create a simple type that implements PartialEq with another type
766 #[derive(Debug, Clone, Copy, PartialEq, Default)]
767 struct SimpleItem {
768 id: u32,
769 value: u32,
770 }
771
772 #[derive(Debug, Clone, Copy, PartialEq)]
773 struct SimpleItemId(u32);
774
775 impl PartialEq<SimpleItemId> for SimpleItem {
776 fn eq(&self, other: &SimpleItemId) -> bool {
777 self.id == other.0
778 }
779 }
780
781 let mut set = FixedSet::<SimpleItem, 3>::new();
782 let item1 = SimpleItem { id: 1, value: 10 };
783 let item2 = SimpleItem { id: 2, value: 20 };
784
785 set.insert(item1).unwrap();
786 set.insert(item2).unwrap();
787
788 let search_id = SimpleItemId(1);
789
790 // Test contains
791 assert!(set.contains(&search_id));
792 assert!(!set.contains(&SimpleItemId(999)));
793
794 // Test find
795 let found = set.find(&search_id);
796 assert_eq!(found, Some(&item1));
797
798 // Test find_mut
799 let found_mut = set.find_mut(&search_id);
800 assert!(found_mut.is_some());
801 if let Some(found_item) = found_mut {
802 found_item.value = 999;
803 }
804
805 // Verify the modification worked
806 let found_again = set.find(&search_id);
807 assert_eq!(found_again.unwrap().value, 999);
808
809 // Test remove
810 let removed = set.remove(&SimpleItemId(2));
811 assert_eq!(removed, Some(item2));
812 assert!(!set.contains(&SimpleItemId(2)));
813 assert_eq!(set.len(), 1);
814 }
815}