From def3e015c7a8c82430fff20994b75b233c0c423b Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 27 Jun 2016 17:44:05 +0300 Subject: [PATCH 1/3] Added closureScope template --- lib/system.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 5a84f4a522..6a4265e5ab 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): From ecfcf49a9d57c40f32eb019368b32828b86a041b Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 28 Jun 2016 13:07:19 +0300 Subject: [PATCH 2/3] Added a note on closureScope. Added Kyiv :) --- doc/manual/procs.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt index dbd5932869..ea6866845b 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 `_ +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)) From e61cfea78b9ebb8df734c7b9d15d81b50ce76613 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 28 Jun 2016 13:13:37 +0300 Subject: [PATCH 3/3] Fixed broken test. Added closureScope test. --- tests/assert/tunittests.nim | 3 +++ tests/closure/uclosures.nim | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/assert/tunittests.nim b/tests/assert/tunittests.nim index cbbfe63c60..de917511c7 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 817bfec6b8..f259cfeb9c 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 -