1#![cfg_attr(not(test), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4
5use core::cell::Cell;
6use core::cell::UnsafeCell;
7use core::fmt::Debug;
8use core::mem::MaybeUninit;
9use core::panic::Location;
10use core::ptr;
11
12macro_rules! slot_panic {
13 ($self:ident, $msg:expr) => {
14 #[cfg(not(debug_assertions))]
15 {
16 panic!($msg);
17 }
18 #[cfg(debug_assertions)]
19 {
20 let loc = $self.last_modified.get();
21 panic!(
22 "{}\nLast modified at {}:{}:{}",
23 $msg,
24 loc.file(),
25 loc.line(),
26 loc.column()
27 );
28 }
29 };
30}
31
32#[allow(dead_code)]
33fn assert_send<T: Send>() {}
34#[allow(dead_code)]
35fn assert_sync<T: Sync>() {}
36#[allow(dead_code)]
37fn check_traits() {
38 assert_send::<SlotCell<i32>>();
39 }
41
42pub struct SlotCell<T> {
101 is_empty: Cell<bool>,
102 cell: UnsafeCell<MaybeUninit<T>>,
103 #[cfg(debug_assertions)]
104 last_modified: Cell<Location<'static>>,
105}
106
107impl<T> SlotCell<T> {
108 #[inline]
119 #[cfg_attr(debug_assertions, track_caller)]
120 pub fn new(val: T) -> Self {
121 Self {
122 is_empty: Cell::new(false),
123 cell: UnsafeCell::new(MaybeUninit::new(val)),
124 #[cfg(debug_assertions)]
125 last_modified: Cell::new(Location::caller().clone()),
126 }
127 }
128
129 #[inline]
146 #[cfg_attr(debug_assertions, track_caller)]
147 pub fn empty() -> Self {
148 Self {
149 is_empty: Cell::new(true),
150 cell: UnsafeCell::new(MaybeUninit::uninit()),
151 #[cfg(debug_assertions)]
152 last_modified: Cell::new(Location::caller().clone()),
153 }
154 }
155
156 #[must_use]
175 #[inline]
176 #[cfg_attr(debug_assertions, track_caller)]
177 pub fn take(&self) -> T {
178 if self.is_empty.get() {
179 slot_panic!(
180 self,
181 "Attempted to `take` a value when the slot is already empty."
182 );
183 }
184 let val = self.take_unchecked();
185 #[cfg(debug_assertions)]
186 self.last_modified.set(Location::caller().clone());
187 val
188 }
189
190 #[inline(always)]
191 #[cfg_attr(debug_assertions, track_caller)]
192 fn take_unchecked(&self) -> T {
193 debug_assert!(!self.is_empty.get());
194 self.is_empty.set(true);
195 unsafe { ptr::read(self.cell.get()).assume_init() }
196 }
197
198 #[inline]
214 pub fn is_empty(&self) -> bool {
215 self.is_empty.get()
216 }
217
218 #[inline]
234 pub fn is_filled(&self) -> bool {
235 !self.is_empty.get()
236 }
237
238 #[inline]
261 #[cfg_attr(debug_assertions, track_caller)]
262 pub fn put(&self, val: T) {
263 if !self.is_empty.get() {
264 slot_panic!(
265 self,
266 "Attempted to `put` a value when the slot is already filled."
267 );
268 }
269 self.put_unchecked(val);
270 #[cfg(debug_assertions)]
271 self.last_modified.set(Location::caller().clone());
272 }
273
274 #[inline(always)]
275 #[cfg_attr(debug_assertions, track_caller)]
276 fn put_unchecked(&self, val: T) {
277 debug_assert!(self.is_empty.get());
278 unsafe {
279 ptr::write(self.cell.get(), MaybeUninit::new(val));
280 }
281 self.is_empty.set(false);
282 }
283
284 #[inline]
307 #[cfg_attr(debug_assertions, track_caller)]
308 pub fn replace(&self, val: T) -> T {
309 if self.is_empty.get() {
310 slot_panic!(
311 self,
312 "Attempted to `replace` a value when the slot is already empty."
313 );
314 }
315 let val = unsafe { ptr::replace(self.cell.get(), MaybeUninit::new(val)).assume_init() };
316 #[cfg(debug_assertions)]
317 self.last_modified.set(Location::caller().clone());
318 val
319 }
320
321 #[inline]
346 #[cfg_attr(debug_assertions, track_caller)]
347 pub fn swap(&self, other: &Self) {
348 if ptr::eq(self, other) {
349 return;
350 }
351 if self.is_empty.get() {
352 slot_panic!(
353 self,
354 "Attempted to `swap` a value when this slot is already empty."
355 );
356 }
357 if other.is_empty.get() {
358 slot_panic!(
359 self,
360 "Attempted to `swap` a value when the other slot is already empty."
361 );
362 }
363 unsafe {
364 ptr::swap(self.cell.get(), other.cell.get());
365 }
366 #[cfg(debug_assertions)]
367 {
368 let location = Location::caller().clone();
369 self.last_modified.set(location.clone());
370 other.last_modified.set(location);
371 }
372 }
373
374 #[inline]
411 #[cfg_attr(debug_assertions, track_caller)]
412 pub fn with<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
413 let mut v = self.take();
414 let r = f(&mut v);
415 self.put(v);
416 r
417 }
418
419 #[inline]
459 #[cfg_attr(debug_assertions, track_caller)]
460 pub fn update(&self, f: impl FnOnce(T) -> T) {
461 let v = self.take();
462 let r = f(v);
463 self.put(r);
464 }
465
466 #[inline]
485 #[cfg_attr(debug_assertions, track_caller)]
486 pub fn into_inner(self) -> T {
487 self.take()
488 }
489}
490
491impl<T> Drop for SlotCell<T> {
492 #[inline]
493 fn drop(&mut self) {
494 if self.is_empty.get() {
495 return;
496 }
497 self.is_empty.set(true);
498 unsafe {
499 (*self.cell.get()).assume_init_drop();
500 }
501 }
502}
503
504impl<T> Debug for SlotCell<T>
505where
506 T: Debug,
507{
508 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
509 let mut binding = f.debug_struct("SlotCell");
510 let r = if self.is_empty.get() {
511 binding.field("cell", &"EMPTY")
512 } else {
513 let val = self.take();
514 let r = binding.field("cell", &val);
515 self.put(val);
516 r
517 };
518 #[cfg(debug_assertions)]
519 let r = r.field("last_modified", &self.last_modified);
520 r.finish()
521 }
522}
523
524impl<T> Eq for SlotCell<T> where T: Eq {}
525
526impl<T> PartialEq for SlotCell<T>
527where
528 T: PartialEq,
529{
530 fn eq(&self, other: &Self) -> bool {
531 let self_is_empty = self.is_empty.get();
532 if core::ptr::eq(self, other) {
533 if self_is_empty {
536 return true;
537 }
538 let val = self.take();
539 let eq = val == val;
540 self.put(val);
541 return eq;
542 }
543 let other_is_empty = other.is_empty.get();
544 if self_is_empty && other_is_empty {
545 return true;
546 }
547 if self_is_empty || other_is_empty {
548 return false;
549 }
550 let val_a = self.take();
551 let val_b = other.take();
552 let eq = val_a == val_b;
553 self.put(val_a);
554 other.put(val_b);
555 eq
556 }
557}
558
559impl<T> Ord for SlotCell<T>
560where
561 T: Ord,
562{
563 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
564 let self_is_empty = self.is_empty.get();
565 if core::ptr::eq(self, other) {
566 return core::cmp::Ordering::Equal;
569 }
570 let other_is_empty = other.is_empty.get();
571 if self_is_empty && other_is_empty {
572 return core::cmp::Ordering::Equal;
573 }
574 if self_is_empty {
575 return core::cmp::Ordering::Less;
576 }
577 if other_is_empty {
578 return core::cmp::Ordering::Greater;
579 }
580 let val_a = self.take();
581 let val_b = other.take();
582 let ord = val_a.cmp(&val_b);
583 self.put(val_a);
584 other.put(val_b);
585 ord
586 }
587}
588
589impl<T> PartialOrd for SlotCell<T>
590where
591 T: PartialOrd,
592{
593 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
594 let self_is_empty = self.is_empty.get();
595 if core::ptr::eq(self, other) {
596 if self_is_empty {
599 return Some(core::cmp::Ordering::Equal);
600 }
601 let val = self.take();
602 let ord = val.partial_cmp(&val);
603 self.put(val);
604 return ord;
605 }
606 let other_is_empty = other.is_empty.get();
607 if self_is_empty && other_is_empty {
608 return Some(core::cmp::Ordering::Equal);
609 }
610 if self_is_empty {
611 return Some(core::cmp::Ordering::Less);
612 }
613 if other_is_empty {
614 return Some(core::cmp::Ordering::Greater);
615 }
616 let val_a = self.take();
617 let val_b = other.take();
618 let ord = val_a.partial_cmp(&val_b);
619 self.put(val_a);
620 other.put(val_b);
621 ord
622 }
623}
624
625impl<T> core::hash::Hash for SlotCell<T>
626where
627 T: core::hash::Hash,
628{
629 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
630 if self.is_empty.get() {
631 0usize.hash(state);
632 } else {
633 let val = self.take();
634 val.hash(state);
635 self.put(val);
636 }
637 }
638}
639
640impl<T> Default for SlotCell<T> {
641 #[inline]
642 #[cfg_attr(debug_assertions, track_caller)]
643 fn default() -> Self {
644 Self {
645 is_empty: Cell::new(true),
646 cell: UnsafeCell::new(MaybeUninit::uninit()),
647 #[cfg(debug_assertions)]
648 last_modified: Cell::new(Location::caller().clone()),
649 }
650 }
651}
652
653impl<T> From<T> for SlotCell<T> {
654 #[inline]
655 #[cfg_attr(debug_assertions, track_caller)]
656 fn from(value: T) -> Self {
657 Self::new(value)
658 }
659}
660
661impl<T> From<Option<T>> for SlotCell<T> {
662 #[cfg_attr(debug_assertions, track_caller)]
663 fn from(value: Option<T>) -> Self {
664 let (is_empty, cell) = match value {
665 Some(value) => (false, MaybeUninit::new(value)),
666 None => (true, MaybeUninit::uninit()),
667 };
668 Self {
669 is_empty: Cell::new(is_empty),
670 cell: UnsafeCell::new(cell),
671 #[cfg(debug_assertions)]
672 last_modified: Cell::new(Location::caller().clone()),
673 }
674 }
675}
676
677impl<T> From<SlotCell<T>> for Option<T> {
678 fn from(value: SlotCell<T>) -> Self {
679 if value.is_empty() {
680 None
681 } else {
682 Some(value.into_inner())
683 }
684 }
685}
686
687#[cfg(test)]
701mod tests {
702 use core::cmp::Ordering;
703 use std::rc::Rc;
704
705 use super::*;
706
707 #[test]
708 fn test_new_and_take() {
709 let cell = SlotCell::new(10);
710 assert!(!cell.is_empty());
711 assert_eq!(cell.take(), 10);
712 assert!(cell.is_empty());
713 }
714
715 #[test]
716 fn test_empty_and_put() {
717 let cell: SlotCell<i32> = SlotCell::empty();
718 assert!(cell.is_empty());
719 cell.put(20);
720 assert!(!cell.is_empty());
721 assert_eq!(cell.take(), 20);
722 }
723
724 #[test]
725 fn test_replace() {
726 let cell = SlotCell::new(1);
727 let old = cell.replace(2);
728 assert_eq!(old, 1);
729 assert_eq!(cell.take(), 2);
730 }
731
732 #[test]
733 fn test_swap() {
734 let a = SlotCell::new(1);
735 let b = SlotCell::new(2);
736 a.swap(&b);
737 assert_eq!(a.take(), 2);
738 assert_eq!(b.take(), 1);
739 }
740
741 #[test]
742 fn test_into_inner() {
743 let cell = SlotCell::new(String::from("hello"));
744 let s = cell.into_inner();
745 assert_eq!(s, "hello");
746 }
747
748 #[test]
751 #[should_panic]
752 fn test_panic_take_empty() {
753 let cell = SlotCell::new(5);
754 let _ = cell.take();
755 let _ = cell.take(); }
757
758 #[test]
759 #[should_panic]
760 fn test_panic_put_full() {
761 let cell = SlotCell::new(5);
762 cell.put(10);
763 }
764
765 #[test]
766 #[should_panic]
767 fn test_panic_replace_empty() {
768 let cell: SlotCell<i32> = SlotCell::empty();
769 cell.replace(10);
770 }
771
772 #[test]
775 fn test_partial_eq_basics() {
776 let cell_a = SlotCell::new(10);
777 let cell_b = SlotCell::new(10);
778 let cell_c = SlotCell::new(20);
779 let empty_a: SlotCell<i32> = SlotCell::empty();
780 let empty_b: SlotCell<i32> = SlotCell::empty();
781
782 assert_eq!(cell_a, cell_b); assert_ne!(cell_a, cell_c); assert_eq!(empty_a, empty_b); assert_ne!(cell_a, empty_a); }
787
788 #[test]
789 fn test_partial_eq_nan_identity() {
790 let nan = f32::NAN;
791 let cell = SlotCell::new(nan);
792
793 assert!(
795 cell != cell,
796 "SlotCell with NaN should not equal itself via pointer identity"
797 );
798
799 let empty: SlotCell<f32> = SlotCell::empty();
800 assert_eq!(empty, empty, "Empty cells should always equal themselves");
801 }
802
803 #[test]
804 fn test_partial_eq_no_panic_or_ub_on_cycles() {
805 struct Link(Rc<SlotCell<Link>>);
806
807 impl PartialEq for Link {
808 fn eq(&self, _other: &Self) -> bool {
809 *self.0 == *self.0
810 }
811 }
812
813 let cell_a = Rc::new(SlotCell::empty());
814 let cell_b = Rc::new(SlotCell::empty());
815
816 cell_a.put(Link(Rc::clone(&cell_a)));
817 cell_b.put(Link(Rc::clone(&cell_a)));
818
819 let _ = *cell_a == *cell_b;
820 }
821
822 #[test]
825 fn test_ord_total_order() {
826 let small = SlotCell::new(10);
827 let large = SlotCell::new(20);
828 let empty = SlotCell::empty();
829
830 assert_eq!(small.cmp(&large), Ordering::Less);
831 assert_eq!(large.cmp(&small), Ordering::Greater);
832 assert_eq!(small.cmp(&small), Ordering::Equal);
833
834 assert_eq!(empty.cmp(&small), Ordering::Less);
835 assert_eq!(small.cmp(&empty), Ordering::Greater);
836 assert_eq!(empty.cmp(&empty), Ordering::Equal);
837 }
838
839 #[test]
840 fn test_partial_ord_nan() {
841 let nan_cell = SlotCell::new(f32::NAN);
842 let val_cell = SlotCell::new(1.0f32);
843
844 assert_eq!(nan_cell.partial_cmp(&val_cell), None);
845
846 assert_eq!(
847 nan_cell.partial_cmp(&nan_cell),
848 None,
849 "NaN cell identity should be None"
850 );
851 }
852
853 #[test]
854 fn test_ptr_identity_optimization() {
855 let cell = SlotCell::new(5);
856 assert_eq!(cell.cmp(&cell), Ordering::Equal);
857 }
858
859 #[test]
862 fn test_state_integrity_after_compare() {
863 let cell_a = SlotCell::new(100);
864 let cell_b = SlotCell::new(100);
865
866 let _ = cell_a == cell_b;
867 let _ = cell_a.cmp(&cell_b);
868
869 assert!(
870 !cell_a.is_empty.get(),
871 "Cell should be occupied after comparison"
872 );
873 assert!(
874 !cell_b.is_empty.get(),
875 "Cell should be occupied after comparison"
876 );
877
878 let val = cell_a.take_unchecked();
879 assert_eq!(val, 100);
880 }
881
882 #[test]
885 fn test_debug_format() {
886 let cell = SlotCell::new(42);
887 let debug_str = format!("{:?}", cell);
888 assert!(debug_str.contains("SlotCell"));
889 assert!(debug_str.contains("42"));
890 }
891
892 #[test]
893 fn test_slotcell_vs_refcell_size() {
894 use core::cell::Cell;
895 use core::mem::size_of;
896
897 type T = u32;
898
899 let slotcell_size = size_of::<SlotCell<T>>();
900 let cell_option_size = size_of::<Cell<Option<T>>>();
901
902 #[cfg(debug_assertions)]
903 {
904 let location_cell_size = size_of::<Cell<Location<'static>>>();
905
906 assert_eq!(
907 slotcell_size,
908 cell_option_size + location_cell_size,
909 "SlotCell<T> should be Cell<Option<T>> + debug tracking"
910 );
911 }
912
913 #[cfg(not(debug_assertions))]
914 {
915 assert_eq!(
916 slotcell_size, cell_option_size,
917 "SlotCell<T> should be exactly Cell<Option<T>> in release mode"
918 );
919
920 let refcell_size = size_of::<core::cell::RefCell<T>>();
921 assert!(
922 refcell_size > slotcell_size,
923 "RefCell<T> should be larger than SlotCell<T> in release mode. Got {} and {}",
924 refcell_size,
925 slotcell_size
926 );
927 }
928 }
929
930 #[test]
931 fn test_with_mutation() {
932 let cell = SlotCell::new(String::from("Rust"));
933
934 cell.with(|s| {
935 s.push_str(" Programming");
936 });
937
938 assert_eq!(cell.take(), "Rust Programming");
939 }
940
941 #[test]
942 fn test_with_return_value() {
943 let cell = SlotCell::new(42);
944
945 let is_even = cell.with(|v| *v % 2 == 0);
946
947 assert!(is_even);
948 assert_eq!(cell.take(), 42);
949 }
950
951 #[test]
952 fn test_update_transformation() {
953 let cell = SlotCell::new(10);
954
955 cell.update(|v| v + 5);
956
957 assert_eq!(cell.take(), 15);
958 }
959
960 #[test]
961 fn test_update_string_buffer() {
962 let cell = SlotCell::new(vec![1, 2]);
963
964 cell.update(|mut v| {
965 v.push(3);
966 v
967 });
968
969 assert_eq!(cell.take(), vec![1, 2, 3]);
970 }
971
972 #[test]
973 #[should_panic]
974 fn test_panic_with_empty() {
975 let cell: SlotCell<i32> = SlotCell::empty();
976 cell.with(|v| *v += 1);
977 }
978
979 #[test]
980 #[should_panic]
981 fn test_panic_update_empty() {
982 let cell: SlotCell<i32> = SlotCell::empty();
983 cell.update(|v| v + 1);
984 }
985
986 #[test]
987 fn test_with_panic_safety() {
988 let cell = SlotCell::new(vec![1, 2, 3]);
989
990 let result = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
991 cell.with(|_v| {
992 panic!("intentional panic");
993 });
994 }));
995
996 assert!(result.is_err());
997 assert!(cell.is_empty());
998 }
999
1000 #[test]
1001 fn test_update_panic_safety() {
1002 let cell = SlotCell::new(vec![1, 2, 3]);
1003
1004 let result = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
1005 cell.update(|_v| {
1006 panic!("intentional panic");
1007 });
1008 }));
1009
1010 assert!(result.is_err());
1011 assert!(cell.is_empty());
1012 }
1013}