References, Pointers and the Heap

12.1 ref (Managed References)

ref objects are managed by ARC/ORC: freed deterministically when the last reference goes away (cycles are collected by ORC).

Dereference with []: head[] gives the underlying object.

nil dereference raises NilAccessDefect (or a segfault in danger mode).

type
  Node = ref object
    value: int
    next: Node

  # or: NodeObj = object ...; Node = ref NodeObj

var head = Node(value: 1)
head.next = Node(value: 2)
var alias = head          # both point to the same object (reference semantics)
alias.value = 99
echo head.value           # 99
if head.next.next == nil: echo "end of list"
let n = new(Node)         # also allocates

12.2 ptr (Unmanaged Pointers)

alloc, alloc0, realloc, dealloc, plus the createShared / allocShared variants for threads.

ptr is unsafe. Use it for FFI and low-level code.

var x = 10
let p: ptr int = addr x
p[] = 20
echo x                    # 20

let mem = cast[ptr UncheckedArray[int]](alloc0(10 * sizeof(int)))
mem[3] = 42
dealloc(mem)

12.3 Value vs Reference Semantics Summary

int, float, object, tuple, array Stack (or inline) Value (copy, or move when possible) Heap buffer, managed Reference (shared) Reference (raw)

12.4 Nil Safety

The experimental --experimental:strictNotNil and not nil annotations: type SafeNode = ref NodeObj not nil.

Option[T] (std/options) for optional values:

import std/options
proc findUser(id: int): Option[string] =
  if id == 1: some("Ana") else: none(string)
let u = findUser(2)
if u.isSome: echo u.get
echo u.get("anonymous")        # default value