Skip to content

Instantly share code, notes, and snippets.

@itsfrank
Last active October 22, 2024 17:00
Show Gist options
  • Save itsfrank/2994e70569ca358b8ddd1596002c2c29 to your computer and use it in GitHub Desktop.
Save itsfrank/2994e70569ca358b8ddd1596002c2c29 to your computer and use it in GitHub Desktop.
Silly rust gc wrappers
#![feature(coerce_unsized, unsize)]
use std::cell::{Ref, RefCell, RefMut};
use std::marker::Unsize;
use std::ops::CoerceUnsized;
use std::rc::{Rc, Weak};
// immutable garbage-collected pointer
#[derive(Debug, Default)]
pub struct Gc<T: ?Sized> {
inner: Rc<RefCell<T>>,
}
impl<T> Gc<T> {
pub fn new(value: T) -> Self {
Self {
inner: Rc::new(RefCell::new(value)),
}
}
}
impl<T: ?Sized> Gc<T> {
pub fn borrow(&self) -> Ref<T> {
self.inner.borrow()
}
pub fn downgrade(&self) -> GcWeak<T> {
GcWeak {
inner: Rc::downgrade(&self.inner),
}
}
// consider usign Gc! macro instead
pub fn from_rc(value: Rc<RefCell<T>>) -> Self {
Self { inner: value }
}
}
impl<T: ?Sized> From<GcMut<T>> for Gc<T> {
fn from(value: GcMut<T>) -> Self {
Gc { inner: value.inner }
}
}
impl<T: ?Sized> Clone for Gc<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
// garbage-collected pointer with interior mutability
#[derive(Debug, Default)]
pub struct GcMut<T: ?Sized> {
inner: Rc<RefCell<T>>,
}
impl<T> GcMut<T> {
pub fn new(value: T) -> Self {
Self {
inner: Rc::new(RefCell::new(value)),
}
}
}
impl<T: ?Sized> GcMut<T> {
pub fn borrow(&self) -> Ref<T> {
self.inner.borrow()
}
pub fn borrow_mut(&self) -> RefMut<T> {
self.inner.borrow_mut()
}
pub fn downgrade(&self) -> GcWeakMut<T> {
GcWeakMut {
inner: Rc::downgrade(&self.inner),
}
}
// consider usign GcMut! macro instead
pub fn from_rc(value: Rc<RefCell<T>>) -> Self {
Self { inner: value }
}
// you only need this to coerce a concrete type into a dyn type
// let a : GcMut<dyn Bar> = GcMut::new(other.inner_rc()); // where other: GcMut<Foo>
pub fn inner_rc(&self) -> Rc<RefCell<T>> {
self.inner.clone()
}
}
impl<T: ?Sized> Clone for GcMut<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
// weak reference to immutable garbage-collected pointer
#[derive(Debug, Default)]
pub struct GcWeak<T: ?Sized> {
inner: Weak<RefCell<T>>,
}
impl<T: ?Sized> GcWeak<T> {
pub fn upgrade(&self) -> Option<Gc<T>> {
self.inner.upgrade().map(|inner| Gc { inner })
}
}
impl<T: ?Sized> From<GcWeakMut<T>> for GcWeak<T> {
fn from(value: GcWeakMut<T>) -> Self {
GcWeak { inner: value.inner }
}
}
impl<T: ?Sized> Clone for GcWeak<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
// weak reference to garbage-collected pointer with interior mutability
#[derive(Debug, Default)]
pub struct GcWeakMut<T: ?Sized> {
inner: Weak<RefCell<T>>,
}
impl<T: ?Sized> GcWeakMut<T> {
pub fn upgrade(&self) -> Option<GcMut<T>> {
self.inner.upgrade().map(|inner| GcMut { inner })
}
}
impl<T: ?Sized> Clone for GcWeakMut<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
// UNSTABLE: required to allow Gc<dyn Foo> = Gc<Bar> where Bar impls Foo
impl<T: Unsize<U>, U: ?Sized> CoerceUnsized<Gc<U>> for Gc<T> {}
impl<T: Unsize<U>, U: ?Sized> CoerceUnsized<GcMut<U>> for GcMut<T> {}
impl<T: Unsize<U>, U: ?Sized> CoerceUnsized<GcWeak<U>> for GcWeak<T> {}
impl<T: Unsize<U>, U: ?Sized> CoerceUnsized<GcWeakMut<U>> for GcWeakMut<T> {}
#[cfg(test)]
mod tests {
struct Foo {
a: i32,
}
pub trait Bar {
fn get_num(&self) -> i32;
fn set_num(&mut self, v: i32);
}
impl Bar for Foo {
fn get_num(&self) -> i32 {
self.a
}
fn set_num(&mut self, v: i32) {
self.a = v;
}
}
use super::*;
#[test]
fn playground() {
let gc_mut = GcMut::new(10); // mutable gc ptr
let weak_mut = gc_mut.downgrade(); // weak
// side-grade into immutable gc ptr
let gc: Gc<i32> = gc_mut.clone().into();
let weak = gc.downgrade();
let _also_weak: GcWeak<i32> = weak_mut.clone().into(); // can make immut weaks from mut weaks
// upgrate into immutable and mut gc ptr respectively
let also_gc = weak.upgrade().unwrap();
let also_gc_mut = weak_mut.upgrade().unwrap();
assert!(*gc.borrow() == 10);
assert!(*gc.borrow() == *also_gc.borrow());
assert!(*gc.borrow() == *gc_mut.borrow());
*also_gc_mut.borrow_mut() = 13;
assert!(*gc.borrow() == 13);
}
#[test]
fn dyn_playground() {
let gc_mut: GcMut<dyn Bar> = GcMut::new(Foo { a: 1 });
let weak_mut = gc_mut.downgrade(); // weak
// side-grade into immutable gc ptr
let gc: Gc<dyn Bar> = gc_mut.clone().into();
let weak = gc.downgrade();
let _also_weak: GcWeak<dyn Bar> = weak_mut.clone().into(); // can make immut weaks from mut weaks
// upgrate into immutable and mut gc ptr respectively
let also_gc = weak.upgrade().unwrap();
let also_gc_mut = weak_mut.upgrade().unwrap();
assert!(gc.borrow().get_num() == 10);
assert!(gc.borrow().get_num() == also_gc.borrow().get_num());
assert!(gc.borrow().get_num() == gc_mut.borrow().get_num());
also_gc_mut.borrow_mut().set_num(13);
assert!(gc.borrow().get_num() == 13);
}
#[test]
fn coersion_test() {
let gcmut_con = GcMut::new(Foo { a: 1 });
let _gcmut_dyn: GcMut<dyn Bar> = gcmut_con.clone();
let gc_con = Gc::new(Foo { a: 1 });
let _gc_dyn: Gc<dyn Bar> = gc_con.clone();
let gcweakmut_con = gcmut_con.downgrade();
let _gcweakmut_dyn: GcWeakMut<dyn Bar> = gcweakmut_con.clone();
let gcweak_con = gc_con.downgrade();
let _gcweak_dyn: GcWeak<dyn Bar> = gcweak_con.clone();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment