Fearless Concurrency

Handling concurrent programming safely and efficiently is one of Rust's major goals, and Oxide inherits all of these powerful guarantees. Concurrent programming, where different parts of a program execute independently, and parallel programming, where different parts execute at the same time, are becoming increasingly important as computers take advantage of multiple processors.

Historically, programming in these contexts has been difficult and error-prone: it's notoriously hard to get right, and debugging concurrent bugs can feel like chasing ghosts. Rust's ownership and type checking systems are a powerful set of tools to help manage memory safety and concurrency problems. By leveraging ownership and type checking, many concurrency errors are caught at compile time rather than at runtime. We call this approach fearless concurrency.

Fearless concurrency allows you to write code that is free of subtle bugs and is easy to refactor without introducing new bugs.

What You'll Learn

This chapter covers:

  1. Threads - How to create threads to run multiple pieces of code at the same time
  2. Message Passing - Using channels to send messages between threads, following the principle "Do not communicate by sharing memory; share memory by communicating"
  3. Shared State - Using Mutex and Arc for multiple threads to access the same data safely
  4. The Sync and Send Traits - How Oxide extends its concurrency guarantees to user-defined types

Oxide's Concurrency Model

Oxide inherits Rust's full concurrency model without changes. The syntax for working with threads, channels, mutexes, and atomic types is largely the same, with Oxide's standard syntactic differences:

ConceptOxide SyntaxNotes
Import thread moduleimport std.threadDot notation for paths
Spawn with closurethread.spawn { ... }Trailing closure syntax
Move closuremove { ... }Move keyword before brace
Channel creationmpsc.channel()Dot notation
Arc cloningArc.clone(&counter)Associated function call

The ownership rules that make Rust's concurrency safe are preserved exactly in Oxide. When you spawn a thread with a closure, the borrow checker ensures you either move ownership into the thread or use thread-safe reference types like Arc<T>.

Why Fearless?

Many languages provide tools for handling concurrent problems, but Rust (and by extension, Oxide) is different: the type system catches concurrency bugs at compile time. Consider this: if you write concurrent code in other languages and make a mistake, you might not discover the bug until your code is running in production under heavy load. In Oxide, the compiler catches these bugs before your code even runs.

Here's what the type system prevents:

  • Data races - Two threads accessing the same memory where at least one is writing, without synchronization
  • Dangling references - A thread holding a reference to data that another thread has freed
  • Use after move - Accessing data that has been moved into another thread

The rest of this chapter explores how to use threads effectively while letting the compiler ensure your concurrent code is correct.

A Note on Async

This chapter focuses on OS threads using std.thread. Oxide also supports asynchronous programming with async/await, which provides a different model for concurrency. Async programming is covered separately and uses Oxide's prefix await syntax:

// Oxide uses prefix await
let result = await fetchData(url)?

For now, let's dive into thread-based concurrency.