Rugo makes concurrency approachable with two primitives: spawn for individual
tasks and parallel for fan-out work. Both compile to Go goroutines under the
hood.
Spawn: Fire and Collect
spawn runs an expression in a background goroutine and returns a task handle.
Call .value to wait for the result.
use "conv"
a = spawn 2 + 2
b = spawn 3 * 3
puts "a = #{conv.to_s(a.value)}"
puts "b = #{conv.to_s(b.value)}"
a = 4
b = 9
Both computations run concurrently. .value blocks until the result is ready.
This is the simplest concurrency pattern — launch work, collect results.
Parallel: Fan-Out, Collect All
When you have multiple independent operations, parallel runs them all and
returns an ordered array of results.
use "conv"
results = parallel
1 * 10
2 * 10
3 * 10
end
for i, r in results
puts "result #{conv.to_s(i)}: #{conv.to_s(r)}"
end
result 0: 10
result 1: 20
result 2: 30
Results come back in the same order as the expressions — no matter which
goroutine finishes first. This makes parallel predictable and easy to reason
about.
Polling with .done
Sometimes you don't want to block. Use .done to check if a task has
finished without waiting.
use "conv"
task = spawn 42
# Wait for it to complete
`sleep 0.1`
puts "done: #{conv.to_s(task.done)}"
puts "value: #{conv.to_s(task.value)}"
done: true
value: 42
This is useful for building progress indicators, non-blocking UIs, or multiplexing multiple tasks.
Timeouts with .wait
For operations that might hang, .wait(seconds) blocks up to a deadline and
panics on timeout. Pair it with try/or for clean handling.
task = spawn `sleep 5`
result = try task.wait(1) or "timed out"
puts result
timed out
This pattern is essential for network requests, external processes, and anything where you can't trust the other side to respond promptly.
The Concurrency Idiom
Idiomatic concurrent Rugo follows a simple recipe:
- Independent work →
parallel(fan-out, collect all) - Single background task →
spawn+.value - Unreliable work →
spawn+try task.wait(n) or fallback - Fire and forget →
spawnwith no assignment
Always pair spawn with try/or when the spawned work can fail. Panics inside
a spawned task are captured and re-raised when you call .value — wrapping it
in try catches them cleanly.