diff options
-rw-r--r-- | doc/manual/procs.txt | 8 | ||||
-rw-r--r-- | lib/system.nim | 21 | ||||
-rw-r--r-- | tests/assert/tunittests.nim | 3 | ||||
-rw-r--r-- | tests/closure/uclosures.nim | 15 |
4 files changed, 44 insertions, 3 deletions
diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt index dbd593286..ea6866845 100644 --- a/doc/manual/procs.txt +++ b/doc/manual/procs.txt @@ -215,6 +215,12 @@ the closure and its enclosing scope (i.e. any modifications made to them are visible in both places). The closure environment may be allocated on the heap or on the stack if the compiler determines that this would be safe. +Creating closures in loops +~~~~~~~~~~~~~~~~ + +Since closures capture local variables by reference it is often not wanted +behavior inside loop bodies. See `closureScope <system.html#closureScope>`_ +for details on how to change this behavior. Anonymous Procs --------------- @@ -223,7 +229,7 @@ Procs can also be treated as expressions, in which case it's allowed to omit the proc's name. .. code-block:: nim - var cities = @["Frankfurt", "Tokyo", "New York"] + var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] cities.sort(proc (x,y: string): int = cmp(x.len, y.len)) diff --git a/lib/system.nim b/lib/system.nim index 5a84f4a52..6a4265e5a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3633,6 +3633,27 @@ proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect, elif x.isNil or y.isNil: result = false else: result = strcmp(x, y) == 0 +template closureScope*(body: untyped): stmt = + ## Useful when creating a closure in a loop to capture local loop variables by + ## their current iteration values. Example: + ## + ## .. code-block:: nim + ## var myClosure : proc() + ## # without closureScope: + ## for i in 0 .. 5: + ## let j = i + ## if j == 3: + ## myClosure = proc() = echo j + ## myClosure() # outputs 5. `j` is changed after closure creation + ## # with closureScope: + ## for i in 0 .. 5: + ## closureScope: # Everything in this scope is locked after closure creation + ## let j = i + ## if j == 3: + ## myClosure = proc() = echo j + ## myClosure() # outputs 3 + (proc() = body)() + {.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.} when defined(nimconfig): diff --git a/tests/assert/tunittests.nim b/tests/assert/tunittests.nim index cbbfe63c6..de917511c 100644 --- a/tests/assert/tunittests.nim +++ b/tests/assert/tunittests.nim @@ -1 +1,4 @@ +discard """ +output: "" +""" import "../template/utemplates", "../closure/uclosures" diff --git a/tests/closure/uclosures.nim b/tests/closure/uclosures.nim index 817bfec6b..f259cfeb9 100644 --- a/tests/closure/uclosures.nim +++ b/tests/closure/uclosures.nim @@ -1,12 +1,23 @@ +# This test is included from within tunittests import unittest -test "loop variables are captured by copy": +test "loop variables are captured by ref": var funcs: seq[proc (): int {.closure.}] = @[] for i in 0..10: let ii = i funcs.add do -> int: return ii * ii + check funcs[0]() == 100 + check funcs[3]() == 100 + +test "loop variables in closureScope are captured by copy": + var funcs: seq[proc (): int {.closure.}] = @[] + + for i in 0..10: + closureScope: + let ii = i + funcs.add do -> int: return ii * ii + check funcs[0]() == 0 check funcs[3]() == 9 - |