type Rc[T] = object data: ptr T refs: ptr int proc rc[T](value: sink T): Rc[T] = result.data = cast[ptr T](alloc(sizeof(T))) copyMem(result.data, addr value, sizeof(T)) result.refs = cast[ptr int](alloc(sizeof(int))) result.refs[] = 1 proc `=copy`[T](dest: var Rc[T]; source: Rc[T]) = `=destroy`(dest) wasMoved(dest) copyMem(addr dest, addr source, sizeof(Rc[T])) inc dest.refs[] proc `=destroy`[T](self: Rc[T]) = if self.refs == nil: return dec self.refs[] if self.refs[] == 0: dealloc(self.data) dealloc(self.refs) proc `[]`[T](self: Rc[T]): var T = self.data[] type Person = object name: string var original = Person(name: "Soreto") var a = rc(original) block: var b = a echo "a: " & a[].name echo "b: " & b[].name echo "refs: " & $a.refs[] b[].name = "Paulo" echo "a: " & a[].name echo "b: " & b[].name echo "refs: " & $a.refs[] echo "a: " & a[].name echo "refs: " & $a.refs[] echo "original: " & original.name Run
I'm a little confused about those three lines: proc rc[T](value: sink T): Rc[T] = result.data = cast[ptr T](alloc(sizeof(T))) copyMem(result.data, addr value, sizeof(T)) Run It feels like I'm making two copies: the first one because of the sink (to prevent using the value after creating the Rc[T]), and another one to move it to the heap. Is there an alternative way to achieve this? I'm asking for educational purposes, as Nim already has this functionality. Another thing that is bothering me is the fact that I can still use `original` after calling `rc(original)`. What am I doing wrong here? After writing this, I came across another puzzling issue. Inside the `=destroy` hook, I need to check if the pointer is nil. The problem is, I can't figure out when this would be called with a nil pointer.