Skip to content

Move values out of Future on await #601

@arnetheduck

Description

@arnetheduck

Consider the following code:

proc f(): Future[string] {.async.} =
  repeat('c', 10)

proc g(): Future[string] {.async.} =
  await f()

let x = waitFor g()

What practically happens is:

  • in the body of f, a string is allocated and assigned to the returned Future
  • await f makes a copy of that string and assigns it to the future returned by g
  • waitFor g() makes another copy of the string and assigns it to x

In both cases, the value inside the returned Future is copied because await cannot be sure that it owns the future - that the Future is not used elsewhere.

Lacking knowledge unique ownership at compile time, we have a few options to address this:

  • return a Future[ref string] instead - causes an additional allocation for the ref string
  • make Future.value return a var T instead of lent T - this allows let x = move(waitFor g()), ie the caller can explicitly choose to move the value out of the future
    • similarly, we can call the mutable version mvalue and have await / waitFor use the mutable version
    • ugly because caller has to remember to move, even though 99% of usages don't need it
  • make await/waitFor inject the move automatically
    • If the future has multiple references, this will break existing code that expects the value to stay in the future - for example:
    let f = g()
    let a = await f
    let b = await f
  • add mawait or similarly named alternative that performs the move, leaving the existing await as-is
    • difficult to teach, reasonable only for legacy compat

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions