Var return type

A proc, converter or iterator may return a var type which means that the returned value is an l-value and can be modified by the caller:

  1. var g = 0
  2. proc writeAccessToG(): var int =
  3. result = g
  4. writeAccessToG() = 6
  5. assert g == 6

It is a static error if the implicitly introduced pointer could be used to access a location beyond its lifetime:

  1. proc writeAccessToG(): var int =
  2. var g = 0
  3. result = g # Error!

For iterators, a component of a tuple return type can have a var type too:

  1. iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
  2. for i in 0..a.high:
  3. yield (i, a[i])

In the standard library every name of a routine that returns a var type starts with the prefix m per convention.

Memory safety for returning by var T is ensured by a simple borrowing rule: If result does not refer to a location pointing to the heap (that is in result = X the X involves a ptr or ref access) then it has to be derived from the routine’s first parameter:

  1. proc forward[T](x: var T): var T =
  2. result = x # ok, derived from the first parameter.
  3. proc p(param: var int): var int =
  4. var x: int
  5. # we know 'forward' provides a view into the location derived from
  6. # its first argument 'x'.
  7. result = forward(x) # Error: location is derived from ``x``
  8. # which is not p's first parameter and lives
  9. # on the stack.

In other words, the lifetime of what result points to is attached to the lifetime of the first parameter and that is enough knowledge to verify memory safety at the callsite.

Future directions

Later versions of Nim can be more precise about the borrowing rule with a syntax like:

  1. proc foo(other: Y; container: var X): var T from container

Here var T from container explicitly exposes that the location is derived from the second parameter (called ‘container’ in this case). The syntax var T from p specifies a type varTy[T, 2] which is incompatible with varTy[T, 1].