---
title: "22.4 Send and Sync"
description: "The two marker traits the compiler uses to decide what's safe to move or share between threads, and why concurrency safety is automatic."
url: "https://learnrust.net/chapter-22/send-and-sync/"
last_updated: "2026-06-13"
---

# 22.4 Send and Sync

In the last three lessons the compiler quietly made some choices for you. It let you `move` a `Vec` into a thread, but it would have rejected an `Rc`. It let you share an `Arc<Mutex<T>>` across threads, but not a bare `Mutex` wrapped in `Rc`. *How does it know?* The answer is two **marker traits**, `Send` and `Sync`, that classify every type by what's safe to do with it across threads. You'll almost never write them yourself; understanding them is about understanding what the compiler is actually checking when it makes concurrency "fearless."

## Marker traits

A **marker trait** is a trait with no methods. It doesn't add behavior; it just *marks* a type as having some property, a label the compiler can check. `Send` and `Sync` are the two markers that matter for threads:

A type is **`Send`** if it's safe to **move** to another thread, to transfer ownership across a thread boundary. Most types are `Send`. If a thread can take ownership of a value, that value is `Send`.

A type is **`Sync`** if it's safe to **share** between threads by reference, if `&T` can be accessed from multiple threads at once. A type `T` is `Sync` exactly when `&T` is `Send`.

The distinction is move-it versus share-a-reference-to-it. When you `move` a `Vec` into a thread (lesson [22.1](https://learnrust.net/chapter-22/threads/)), the compiler is checking that `Vec` is `Send`. When you share an `Arc<Mutex<T>>` across threads, it's checking the relevant `Sync`/`Send` bounds. These checks are why `thread::spawn` and the channel and mutex types have the bounds they do; the safety isn't enforced by runtime checks but by which traits your types implement.

## Almost everything is Send and Sync, automatically

Here's the part that makes this painless: `Send` and `Sync` are **auto traits**. The compiler implements them for your types *automatically*, based on their fields. A struct is `Send` if all its fields are `Send`; it's `Sync` if all its fields are `Sync`. You implement neither by hand. So when you write:

```rust
struct Point {
    x: i32,
    y: i32,
}
```

`Point` is automatically both `Send` and `Sync`, because `i32` is both, and so a `Point` drops into a thread or an `Arc` with no ceremony. The vast majority of types you'll ever write are `Send` and `Sync` for free, which is why you've been doing concurrency for three lessons without once thinking about these traits. They were working silently the whole time.

## The exceptions tell the story

The interesting cases are the few types that *aren't* `Send` or `Sync`, because each non-implementation is the compiler encoding a real danger you've already met.

`Rc<T>` is **not `Send`**. Recall from lesson [21.5](https://learnrust.net/chapter-21/rc/) why: its reference count is updated non-atomically, so two threads cloning or dropping the same `Rc` at once could corrupt the count, a data race on the counter itself. Because `Rc` isn't `Send`, the compiler refuses to let it cross a thread boundary, and *that's the actual mechanism* behind "use `Arc` for threads." `Arc` *is* `Send` and `Sync` (its count is atomic), so it passes the check `Rc` fails. The advice "use `Arc` across threads" is really "use the type that's `Send`."

`RefCell<T>` is `Send` but **not `Sync`**. Its runtime borrow tracking (lesson [21.6](https://learnrust.net/chapter-21/refcell-and-interior-mutability/)) isn't safe against concurrent access, so sharing `&RefCell` across threads could let two threads both pass the borrow check and race. Not being `Sync` means the compiler won't let you share it. `Mutex<T>`, by contrast, *is* `Sync` (its locking is thread-safe), which is exactly why `Mutex` is the type you share across threads and `RefCell` is the one you don't.

See the pattern? Every "use this type, not that one for threads" rule from chapter 21 is really a `Send`/`Sync` fact. The traits aren't extra rules layered on top; they're the single underlying rule that *generates* all those pieces of advice.

> **Key insight**
>
> "Fearless concurrency" is `Send` and `Sync` doing their job at compile time. The compiler computes these traits automatically from your types' fields, and `thread::spawn`, channels, and `Arc`/`Mutex` carry bounds requiring them. So a type that would be unsafe to share, an `Rc` with its racy counter, a `RefCell` with its racy borrow flag, simply fails to compile when you try to thread it, with a "cannot be sent between threads safely" error. You don't audit for thread safety; the type system did it before the program ran. This is the guarantee no mainstream language before Rust offered.

## When you'd see this

You won't write `impl Send` or `impl Sync` in normal code. Where these traits surface is in *error messages*. When you accidentally try to move a non-`Send` type into a thread, the compiler says something like `` `Rc<i32>` cannot be sent between threads safely `` and points at the `Send` bound on `spawn`. Now you'll recognize what it's telling you: this type lacks the marker that certifies it safe, switch to the thread-safe equivalent (`Rc` to `Arc`, `RefCell` to `Mutex`). The fix is almost always swapping in the `Send`/`Sync` version of the tool, the exact swaps the last two lessons taught.

> **For advanced readers**
>
> You *can* implement `Send`/`Sync` by hand, but only inside `unsafe` (chapter 25), because you're then promising the compiler something it couldn't verify, that your type's internal sharing really is safe. This comes up when wrapping raw pointers or foreign resources where you've manually ensured thread safety. It's rare, expert territory, and the burden of proof is entirely on you. For all ordinary code, the automatic derivation is correct and you should never need to override it.

## Quiz time

**Question #1**

What's the difference between `Send` and `Sync`?

<details class="solution">
<summary>Show solution</summary>

`Send` means a type is safe to *move* (transfer ownership) to another thread. `Sync` means a type is safe to *share* between threads by reference, i.e. `&T` can be accessed from multiple threads at once (formally, `T` is `Sync` iff `&T` is `Send`). Move-it versus share-a-reference-to-it. Both are marker traits (no methods) that the compiler uses to decide what thread operations are allowed.

</details>

**Question #2**

Do you usually implement `Send` and `Sync` yourself? How does a type get them?

<details class="solution">
<summary>Show solution</summary>

No. They're *auto traits*: the compiler implements them automatically for a type when all of its fields are `Send`/`Sync`. So almost every type you write is both, for free, which is why everyday concurrency code never mentions them. You'd only implement them manually inside `unsafe` for exotic types (e.g. wrapping raw pointers), which is rare expert work.

</details>

**Question #3**

`Rc<T>` is not `Send`. How does that single fact explain the rule "use `Arc`, not `Rc`, across threads"?

<details class="solution">
<summary>Show solution</summary>

Because `thread::spawn` (and friends) require the moved data to be `Send`, and `Rc` isn't `Send` (its non-atomic reference count could be corrupted by concurrent access), the compiler rejects any attempt to move an `Rc` into another thread, that's the actual mechanism behind the advice. `Arc` *is* `Send`/`Sync` because its count is atomic, so it passes the bound `Rc` fails. The "use `Arc`" rule is just "use the type that satisfies the `Send` requirement."

</details>

You now understand both *how* to do concurrency safely and *why* the compiler can guarantee it. The final lesson before the quiz ([22.5](https://learnrust.net/chapter-22/data-parallelism-with-rayon/)) shows the ecosystem's victory lap: the `rayon` crate, which turns a sequential iterator into a parallel one by changing a single method name.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
