How can a closure iterator manage resources? In my particular case, I have a
closure iterator that create channels and threads. When the iterator is aborted
from the caller side, how can I free the resources I have allocated in the
iterator?
For instance, consider the following example:
import strutils
import os
proc stream(m: int): iterator: int =
iterator iter: int {.closure.} =
var i = 0
echo "Allocate resources"
while true:
yield i
inc(i)
if i == m:
echo "Iterator max reached; end of iterator"
break
echo "Deallocate resources"
result = iter
proc main =
let iter = stream(parseInt(paramStr(1)))
for i in iter():
echo "i=", i
if i == 10:
echo "Aborting iteration in main after 10 items..."
sleep(2_000)
break
echo "Out of iterator in main"
echo "Before main"
main()
echo "After main"
Run
This code creates an iterator that count up to a maximum specified from the
command line, but after 10 loops the caller procedure aborts the iterator.
If we run it like `./pug 5`, we see that the iterator allocates the resources
and frees them when it naturally completes.
Before main
Allocate resources
i=0
i=1
i=2
i=3
i=4
Iterator max reached; end of iterator
Deallocate resources
Out of iterator in main
After main
Run
But if we run it like `./pug 15`, we see that the deallocation does not happen:
Before main
Allocate resources
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10
Aborting iteration in main after 10 items...
Out of iterator in main
After main
Run
Enclosing the iterator code with a `try: ... finally: ...` does not help
because there are no exceptions in the game. I've looked at the [Nim
Destructors and Move Semantics](https://nim-lang.org/docs/destructors.html)
documentation but that does not seem to apply there either.
So, how do I ensure that resources are freed when a closure iterator is out of
scope?