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()

The compiler can produce a warning in these cases, however this behavior is turned off by default. It can be enabled for a section of code via the warning[ObservableStores] and push/pop pragmas. Take the above code as an example:

  1. {.push warning[ObservableStores]: on.}
  2. main()
  3. {.pop.}