# go
[](https://crates.io/crates/go)
[](https://docs.rs/go)
[](#license)
A runtime-agnostic, Go-style concurrency library for Rust.
`go` gives you the concurrency primitives you reach for in Go — goroutine-style
task spawning, channels, `select`, a `WaitGroup`, timers, and `singleflight` —
on top of `async`/`await`, with the async runtime chosen at compile time.
## Why
- **Familiar.** If you know Go's `go`, `chan`, `select`, and `sync.WaitGroup`,
you already know this API.
- **Runtime-agnostic.** Write your code once and pick the runtime
(`tokio`, `async-std`, or `smol`) with a Cargo feature. Exactly one backend
must be enabled — a missing or ambiguous choice is a compile error, not a
runtime surprise.
- **Small surface.** Thin, well-tested wrappers over `async-channel` and
`event-listener`; no heavyweight abstractions.
## Installation
```toml
[dependencies]
go = { version = "0.1", features = ["rt-tokio"] } # or rt-async-std, rt-smol
```
`rt-tokio` is the default, so `go = "0.1"` selects Tokio. To use a different
runtime, disable defaults:
```toml
go = { version = "0.1", default-features = false, features = ["rt-async-std"] }
```
Requires a Rust toolchain with edition 2024 support.
## Quick start
```rust
use std::time::Duration;
#[tokio::main]
async fn main() {
// Spawn a task (fire-and-forget, or await its result).
let handle = go::spawn(async { 1 + 1 });
assert_eq!(handle.await.unwrap(), 2);
// Channels.
let (tx, rx) = go::chan::bounded(8);
go::spawn(async move {
for i in 0..3 {
tx.send(i).await.unwrap();
}
});
while let Ok(v) = rx.recv().await {
println!("got {v}");
}
// Timers.
go::sleep(Duration::from_millis(10)).await;
}
```
## Primitives
### Spawning — `spawn` / `go!`
```rust
// These are equivalent.
let h1 = go::spawn(async { "hello" });
let h2 = go::go!(async { "hello" });
assert_eq!(h1.await.unwrap(), "hello");
```
Awaiting the returned `JoinHandle` yields `Result<T, JoinError>`; a panicking
task is reported as `JoinError::Panic`. Dropping the handle detaches the task
(it keeps running), matching goroutine semantics.
### Channels — `chan`
```rust
let (tx, rx) = go::chan::bounded(1); // or go::chan::unbounded()
tx.send(42).await.unwrap();
assert_eq!(rx.recv().await.unwrap(), 42);
```
`Sender`/`Receiver` are clonable MPMC handles re-exported from `async-channel`.
### Multiplexing — `select!`
Wait on the first of several channel receives to complete, with an optional
non-blocking `default` branch:
```rust
go::select! {
v = rx1.recv() => println!("from rx1: {v:?}"),
v = rx2.recv() => println!("from rx2: {v:?}"),
default => println!("nothing ready right now"),
}
```
Supports up to three receive branches plus the optional `default`.
### Waiting for a group — `WaitGroup`
```rust
let wg = go::WaitGroup::new();
for i in 0..4 {
wg.add(1);
let wg = wg.clone();
go::spawn(async move {
// ...do work with `i`...
wg.done();
});
}
wg.wait().await; // returns once every task has called done()
```
`wg.guard()` returns a guard that calls `done()` automatically when dropped.
### Timers — `sleep` / `timeout`
```rust
use std::time::Duration;
go::sleep(Duration::from_millis(50)).await;
match go::timeout(Duration::from_secs(1), do_work()).await {
Ok(value) => { /* completed in time */ }
Err(go::Elapsed) => { /* deadline passed */ }
}
```
### Deduplicating work — `singleflight`
Ensure only one execution is in flight for a given key; concurrent callers share
the single result (à la Go's `golang.org/x/sync/singleflight`).
```rust
let group = go::singleflight::Group::new();
// If many tasks call this concurrently with the same key, the closure runs
// once and every caller gets a clone of that one result.
let (value, shared) = group
.do_("user:42", async { fetch_user(42).await })
.await;
// `shared` is true if the value was shared with at least one other caller.
```
Also provides `do_chan` (run the work on the runtime and receive the result over
a channel) and `forget` (evict an in-flight key so the next call re-executes).
If the leader's future is cancelled or panics before producing a value, the
in-flight call is abandoned and waiting callers retry — no caller hangs.
## Runtime selection
| `rt-tokio` | `tokio` |
| `rt-async-std` | `async-std` |
| `rt-smol` | `smol` |
Enable exactly one. The crate emits a `compile_error!` if none or more than one
is selected, so misconfiguration fails fast at build time.
## License
Licensed under either of
- Apache License, Version 2.0
- MIT license
at your option.