NRVO

Note: This section describes the current implementation. This part of the language specification will be changed. See https://github.com/nim-lang/RFCs/issues/230 for more information.

The return value is represented inside the body of a routine as the special result variable. This allows for a mechanism much like C++’s “named return value optimization” (NRVO). NRVO means that the stores to result inside p directly affect the destination dest in let/var dest = p(args) (definition of dest) and also in dest = p(args) (assignment to dest). This is achieved by rewriting dest = p(args) to p’(args, dest) where p’ is a variation of p that returns void and receives a hidden mutable parameter representing result.

Informally:

  1. proc p(): BigT = ...
  2. var x = p()
  3. x = p()
  4. # is roughly turned into:
  5. proc p(result: var BigT) = ...
  6. var x; p(x)
  7. p(x)

Let T’s be p’s return type. NRVO applies for T if sizeof(T) >= N (where N is implementation dependent), in other words, it applies for “big” structures.

If p can raise an exception, NRVO applies regardless. This can produce observable differences in behavior:

  1. type
  2. BigT = array[16, int]
  3. proc p(raiseAt: int): BigT =
  4. for i in 0..high(result):
  5. if i == raiseAt: raise newException(ValueError, "interception")
  6. result[i] = i
  7. proc main =
  8. var x: BigT
  9. try:
  10. x = p(8)
  11. except ValueError:
  12. doAssert x == [0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0]
  13. main()

However, the current implementation produces a warning in these cases. There are different ways to deal with this warning:

  1. Disable the warning via {.push warning[ObservableStores]: off.} … {.pop.}. Then one may need to ensure that p only raises before any stores to result happen.
  2. One can use a temporary helper variable, for example instead of x = p(8) use let tmp = p(8); x = tmp.