When I pass a var parameter to an async function, it says "Error: illegal capture" on compilation.
import asyncdispatch
type Thing = tuple
foo: int
proc asynco(thing: var Thing): Future[void] {.async.} =
thing.foo = 23
await sleepAsync(100)
thing.foo = 42
proc main() {.async.} =
var thing: Thing
thing.foo = 19
await asynco(thing)
echo("foo ",thing.foo);
asyncCheck(main())
runForever()
Ultimately what I want is: depending the name of an incoming message and the value of its name=value pairs, I want to send the trailing data to different places. Like a RedMessage would go in the red folder, and a BlueMessage would go in the blue folder but like, a BlueMessage with Identifier=red would go in the red folder, and a BlueMessage with Trailing=Nope would simply be processed without any trailing data.
so like, a utf-8 stream of
Name1=Value1
Name2=Value2
...
Length=datalength if there's data
Data/End
...data follows...
What I was doing (or trying to do) is once the headers have come in, complete a future dispatching the message object to whatever's waiting for it. That something can then decide on a trailing data handler, and on returning from the Future completion that handler will be used to feed trailing data. The trouble is complete(Future,T) doesn't let the callback return a value (like a trailing data handler for instance.)
So, I was using a field in the Message type to store the trailing data handler, and whatever's waiting for it can assign to that field, whereupon my message reading loop will invoke that field with blocks of data until the trailing data is all done.
...except you can't assign to any object parameter that's not a var parameter. And apparantly var parameters can never be used in async procedures, even when they are invoked with lvalues.
Indeed var parameters in async procedures are not currently supported. I've got a patch to add a FutureVar[T] which will make them possible in this obscure branch, this commit introduces it.
For some back story why this hasn't been merged: It broke asynchttpserver benchmarks with the mark and sweep GC. I did make Araq promise me that I could restore those changes straight after 0.11.2 was released (and that he would fix the GC bug), I simply did not have the time to restore them. If you could create a PR to do so I would really appreciate it!
As for your issue, can't you simply return a copy of Thing?
import asyncdispatch
type Thing = tuple
foo: int
proc asynco(thing: Thing): Future[Thing] {.async.} =
result = thing
result .foo = 23
await sleepAsync(100)
result .foo = 42
proc main() {.async.} =
var thing: Thing
thing.foo = 19
thing = await asynco(thing)
echo("foo ",thing.foo)
asyncCheck(main())
runForever()
(Haven't tested this but should work).
Ah, could mention in the documentation that var parameters aren't supported yet. I thought I was doing something wrong. var parameters and Futures are a much trickier idea, since it makes it harder to predict their state, if you're passing variables to be mutated in undeterministic Futures.
A concern with returning the mutation from the async procedure is it introduces a lot of whole structure copying. I don't know if nim can optimize that to only copy the part of the tuple that updated. Plus there's no convenient syntax I'm aware of like var newNode: Thing = (listeners: addListener(oldNode.listeners) restofthefieldsfrom: oldNode). You could make each mutation just have a pointer to the earlier version, I suppose... assuming initializing fields in a tuple is cheaper than copying them.
Anyway, it'll work to do that, so I'll do that, even if it might be needlessly copying a handful of fields every await or so. Beats copying an entire stack with swapcontext()!