Select in Nim

Nim’s select statement allows you to wait on multiple channel operations. Combining threads and channels with select is a powerful feature of Nim.

import std/[os, strformat]

proc main() =
  let c1 = Channel[string]()
  let c2 = Channel[string]()
  c1.open()
  c2.open()

  # Each channel will receive a value after some amount
  # of time, to simulate e.g. blocking RPC operations
  # executing in concurrent threads.
  proc worker1() {.thread.} =
    sleep(1000)
    c1.send("one")

  proc worker2() {.thread.} =
    sleep(2000)
    c2.send("two")

  var t1, t2: Thread[void]
  createThread(t1, worker1)
  createThread(t2, worker2)

  # We'll use `select` to await both of these values
  # simultaneously, printing each one as it arrives.
  for i in 0..1:
    var msg: string
    select:
    of c1.recv(msg):
      echo fmt"received {msg}"
    of c2.recv(msg):
      echo fmt"received {msg}"

  joinThread(t1)
  joinThread(t2)
  c1.close()
  c2.close()

main()

We receive the values “one” and then “two” as expected.

$ nim c -r select.nim
received one
received two

Note that the total execution time is only ~2 seconds since both the 1 and 2 second sleep operations execute concurrently.

In this Nim version, we use threads instead of goroutines, and we create channels using the Channel type. The select statement in Nim works similarly to Go’s, allowing us to wait on multiple channel operations.

We define two worker procedures that sleep for different durations before sending a message on their respective channels. These are executed in separate threads.

The main loop uses select to wait for messages from either channel, printing them as they arrive. After the loop, we join the threads and close the channels to clean up resources.

This example demonstrates how Nim can handle concurrent operations and channel communication in a way similar to Go, albeit with some syntax and conceptual differences.