Reference and pointer types

References (similar to pointers in other programming languages) are a way to introduce many-to-one relationships. This means different references can point to and modify the same location in memory (also called aliasing).

Nim distinguishes between traced and untraced references. Untraced references are also called pointers. Traced references point to objects of a garbage collected heap, untraced references point to manually allocated objects or to objects somewhere else in memory. Thus untraced references are unsafe. However for certain low-level operations (accessing the hardware) untraced references are unavoidable.

Traced references are declared with the ref keyword, untraced references are declared with the ptr keyword. In general, a ptr T is implicitly convertible to the pointer type.

An empty subscript [] notation can be used to derefer a reference, the addr procedure returns the address of an item. An address is always an untraced reference. Thus the usage of addr is an unsafe feature.

The . (access a tuple/object field operator) and [] (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types:

  1. type
  2. Node = ref NodeObj
  3. NodeObj = object
  4. le, ri: Node
  5. data: int
  6. var
  7. n: Node
  8. new(n)
  9. n.data = 9
  10. # no need to write n[].data; in fact n[].data is highly discouraged!

Automatic dereferencing can be performed for the first argument of a routine call, but this is an experimental feature and is described here.

In order to simplify structural type checking, recursive tuples are not valid:

  1. # invalid recursion
  2. type MyTuple = tuple[a: ref MyTuple]

Likewise T = ref T is an invalid type.

As a syntactical extension object types can be anonymous if declared in a type section via the ref object or ptr object notations. This feature is useful if an object should only gain reference semantics:

  1. type
  2. Node = ref object
  3. le, ri: Node
  4. data: int

To allocate a new traced object, the built-in procedure new has to be used. To deal with untraced memory, the procedures alloc, dealloc and realloc can be used. The documentation of the system module contains further information.

Nil —-

If a reference points to nothing, it has the value nil. nil is the default value for all ref and ptr types. The nil value can also be used like any other literal value. For example, it can be used in an assignment like myRef = nil.

Dereferencing nil is an unrecoverable fatal runtime error (and not a panic).

A successful dereferencing operation p[] implies that p is not nil. This can be exploited by the implementation to optimize code like:

  1. p[].field = 3
  2. if p != nil:
  3. # if p were nil, ``p[]`` would have caused a crash already,
  4. # so we know ``p`` is always not nil here.
  5. action()

Into:

  1. p[].field = 3
  2. action()

Note: This is not comparable to C’s “undefined behavior” for dereferencing NULL pointers.