1use std::{cell::RefCell, fmt, rc::Rc};
2
3pub struct GC<T>(pub Rc<RefCell<T>>);
4
5impl<T> GC<T> {
6 pub fn new(value: T) -> Self {
7 GC(Rc::new(RefCell::new(value)))
8 }
9
10 pub fn borrow(&self) -> std::cell::Ref<T> {
11 self.0.borrow()
12 }
13
14 pub fn borrow_mut(&self) -> std::cell::RefMut<T> {
15 self.0.borrow_mut()
16 }
17
18 pub fn strong_count(&self) -> usize {
19 Rc::strong_count(&self.0)
20 }
21
22 pub fn weak_count(&self) -> usize {
23 Rc::weak_count(&self.0)
24 }
25
26 pub fn downgrade(&self) -> std::rc::Weak<RefCell<T>> {
27 Rc::downgrade(&self.0)
28 }
29
30 pub fn upgrade(weak: &std::rc::Weak<RefCell<T>>) -> Option<Self> {
31 weak.upgrade().map(GC)
32 }
33}
34
35impl<T> fmt::Debug for GC<T>
36where
37 T: fmt::Debug,
38{
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 self.0.borrow().fmt(f)
41 }
42}
43
44impl<T> Clone for GC<T> {
45 fn clone(&self) -> Self {
46 GC(self.0.clone())
47 }
48}
49
50
51#[cfg(test)]
52mod tests {
53 use std::rc::Weak;
54
55 use super::*;
56
57 #[test]
58 fn test_gc() {
59 let _x = GC::new(5);
60 let _y = GC::new("Hello, world!");
61 let _z = GC::new(vec![1, 2, 3, 4, 5]);
62
63 struct Tracked {
64 id: u32,
65 }
66
67 impl Drop for Tracked {
68 fn drop(&mut self) {
69 println!("Tracked object {} is being dropped!", self.id);
70 }
71 }
72
73 {
74 let obj1 = GC::new(Tracked { id: 1 });
75
76 {
77 let obj2 = GC::new(Tracked { id: 2 });
78
79 let _obj1_ref1 = obj1.clone();
80 let _obj1_ref2 = obj1.clone();
81
82 assert_eq!(obj1.strong_count(), 3);
83 assert_eq!(obj2.strong_count(), 1);
84
85
86 } assert_eq!(obj1.strong_count(), 1);
88 } struct TrackableResource {
93 id: u32,
94 }
95
96 impl Drop for TrackableResource {
97 fn drop(&mut self) {
98 println!("Resource {} being dropped!", self.id);
99 }
100 }
101
102 let tracker: RefCell<Vec<Weak<RefCell<TrackableResource>>>> = RefCell::new(Vec::new());
104
105 {
107 let obj = GC::new(TrackableResource { id: 42 });
108
109 tracker.borrow_mut().push(Rc::downgrade(&obj.0));
111
112 assert_eq!(obj.strong_count(), 1);
113 assert_eq!(tracker.borrow()[0].upgrade().is_some(), true);
114 }
115
116 assert_eq!(tracker.borrow()[0].upgrade().is_some(), false);
117
118 }
119}