use std::{cell::RefCell, fmt, rc::Rc};
pub struct GC<T>(pub Rc<RefCell<T>>);
impl<T> GC<T> {
pub fn new(value: T) -> Self {
GC(Rc::new(RefCell::new(value)))
}
pub fn borrow(&self) -> std::cell::Ref<T> {
self.0.borrow()
}
pub fn borrow_mut(&self) -> std::cell::RefMut<T> {
self.0.borrow_mut()
}
pub fn strong_count(&self) -> usize {
Rc::strong_count(&self.0)
}
pub fn weak_count(&self) -> usize {
Rc::weak_count(&self.0)
}
pub fn downgrade(&self) -> std::rc::Weak<RefCell<T>> {
Rc::downgrade(&self.0)
}
pub fn upgrade(weak: &std::rc::Weak<RefCell<T>>) -> Option<Self> {
weak.upgrade().map(GC)
}
}
impl<T> fmt::Debug for GC<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.borrow().fmt(f)
}
}
impl<T> Clone for GC<T> {
fn clone(&self) -> Self {
GC(self.0.clone())
}
}
#[cfg(test)]
mod tests {
use std::rc::Weak;
use super::*;
#[test]
fn test_gc() {
let _x = GC::new(5);
let _y = GC::new("Hello, world!");
let _z = GC::new(vec![1, 2, 3, 4, 5]);
struct Tracked {
id: u32,
}
impl Drop for Tracked {
fn drop(&mut self) {
println!("Tracked object {} is being dropped!", self.id);
}
}
{
let obj1 = GC::new(Tracked { id: 1 });
{
let obj2 = GC::new(Tracked { id: 2 });
let _obj1_ref1 = obj1.clone();
let _obj1_ref2 = obj1.clone();
assert_eq!(obj1.strong_count(), 3);
assert_eq!(obj2.strong_count(), 1);
} assert_eq!(obj1.strong_count(), 1);
}
struct TrackableResource {
id: u32,
}
impl Drop for TrackableResource {
fn drop(&mut self) {
println!("Resource {} being dropped!", self.id);
}
}
let tracker: RefCell<Vec<Weak<RefCell<TrackableResource>>>> = RefCell::new(Vec::new());
{
let obj = GC::new(TrackableResource { id: 42 });
tracker.borrow_mut().push(Rc::downgrade(&obj.0));
assert_eq!(obj.strong_count(), 1);
assert_eq!(tracker.borrow()[0].upgrade().is_some(), true);
}
assert_eq!(tracker.borrow()[0].upgrade().is_some(), false);
}
}