Concurrency and context: goroutines, channels, and cancellation
Learn Go's concurrency model for Rust developers used to threads or async runtimes.
by the end of this lesson you can
- →Chooses sequential or concurrent execution intentionally
- →Uses goroutines only when they help
- →Accounts for cancellation or request lifetime with context
Overview
Rust developers may arrive from OS threads, channels, Tokio, or other async runtimes. Go treats concurrency as a more ordinary part of application structure, with goroutines, channels, and context used to coordinate work and cancellation.
In Rust, you often
use threads, channels, or async runtimes with explicit ownership and trait bounds governing what can move or be shared.
In Go, the common pattern is
to launch goroutines freely, coordinate with channels when needed, and propagate cancellation and deadlines through context.
why this difference matters
This lesson helps Rust developers stop mapping everything to threads or futures and start reading Go concurrency on its own terms.
Rust
std::thread::spawn(move || {
worker(jobs);
});Go
go worker(ctx, jobs)Deeper comparison
Rust version
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
tx.send(load()).unwrap();
});
let result = rx.recv().unwrap();Go version
resultCh := make(chan Result)
go func() {
resultCh <- load()
}()
result := <-resultChReflect
When does Go concurrency feel simpler than Rust's models, and what safety guarantees are you giving up in exchange?
what a strong answer notices
A strong answer mentions lightweight startup, easy cancellation flow with context, and fewer compiler-enforced guarantees around shared state.
Rewrite
Translate this Rust concurrent shape into Go using goroutines, channels, or context where appropriate.
Rewrite this Rust
async fn load_dashboard(id: UserId) -> Result<Dashboard, Error> {
let profile = load_profile(id).await?;
let billing = load_billing(id).await?;
Ok(build_dashboard(profile, billing))
}what good looks like
- Chooses sequential or concurrent execution intentionally
- Uses goroutines only when they help
- Accounts for cancellation or request lifetime with context
Practice
Design a Go background worker with cancellation support for a Rust developer used to thread joins or async tasks.
success criteria
- Uses context for cancellation
- Explains where channels help and where they are unnecessary
- Keeps concurrency structure simple and readable
Common mistakes
- Expecting goroutines to map neatly onto Rust threads or futures.
- Ignoring context because Rust cancellation patterns looked different.
- Assuming Go concurrency safety is enforced as strongly as Rust's.
takeaways
- ●This lesson helps Rust developers stop mapping everything to threads or futures and start reading Go concurrency on its own terms.
- ●A strong answer mentions lightweight startup, easy cancellation flow with context, and fewer compiler-enforced guarantees around shared state.
- ●Uses context for cancellation