// Ordinary Rust code, not Verus
struct InnerRc<T> {
rc_cell: std::cell::UnsafeCell<u64>,
t: T,
}
struct Rc<T> {
ptr: *mut InnerRc<T>,
}
impl<T> Rc<T> {
fn new(t: T) -> Self {
// Allocate a new InnerRc object, initialize the counter to 1,
// and return a pointer to it.
let rc_cell = std::cell::UnsafeCell::new(1);
let inner_rc = InnerRc { rc_cell, t };
let ptr = Box::leak(Box::new(inner_rc));
Rc { ptr }
}
fn clone(&self) -> Self {
unsafe {
// Increment the counter.
// If incrementing the counter would lead to overflow, then abort.
let inner_rc = &*self.ptr;
let count = *inner_rc.rc_cell.get();
if count == 0xffffffffffffffff {
std::process::abort();
}
*inner_rc.rc_cell.get() = count + 1;
}
// Return a new Rc object with the same pointer.
Rc { ptr: self.ptr }
}
fn drop(self) {
unsafe {
// Decrement the counter.
let inner_rc = &*self.ptr;
let count = *inner_rc.rc_cell.get() - 1;
*inner_rc.rc_cell.get() = count;
// If the counter hits 0, drop the `T` and deallocate the memory.
if count == 0 {
std::ptr::drop_in_place(&mut (*self.ptr).t);
std::alloc::dealloc(self.ptr as *mut u8, std::alloc::Layout::for_value(&*self.ptr));
}
}
}
fn borrow(&self) -> &T {
unsafe { &(*self.ptr).t }
}
}
// Example usage
enum Sequence<V> {
Nil,
Cons(V, Rc<Sequence<V>>),
}
fn main() {
let nil = Rc::new(Sequence::Nil);
let nil_clone = nil.clone();
let a5 = Rc::new(Sequence::Cons(5, nil.clone()));
let a7 = Rc::new(Sequence::Cons(7, nil.clone()));
let a67 = Rc::new(Sequence::Cons(6, a7.clone()));
let x1 = nil.borrow();
let x2 = nil_clone.borrow();
match x1 {
Sequence::Nil => {}
Sequence::Cons(_, _) => {
assert!(false);
}
}
match x2 {
Sequence::Nil => {}
Sequence::Cons(_, _) => {
assert!(false);
}
}
nil.drop();
nil_clone.drop();
a5.drop();
a7.drop();
a67.drop();
}