---
title: Send 与 Sync
url: https://doc.liz6.com/rust/06-concurrency-and-asynchrony/01-Send-Sync
locale: zh
area: rust
tags:
- rust
- 并发与异步
date: 2026-06-30
modified: 2026-06-30
description: Send 和 Sync 是 Rust 并发安全的基石——Send 允许类型跨线程转移所有权,Sync 允许类型跨线程共享引用。它们是 auto trait(编译器自动推导):一个类型的所有字段都是 Send,这个类型就自动是 Send。PhantomData 用来手动静音或恢复自动推导——在不安全代码和 FFI 里至…
---

# Send 与 Sync

> Send 和 Sync 是 Rust 并发安全的基石——Send 允许类型跨线程转移所有权,Sync 允许类型跨线程共享引用。它们是 auto trait(编译器自动推导):一个类型的所有字段都是 Send,这个类型就自动是 Send。PhantomData 用来手动静音或恢复自动推导——在不安全代码和 FFI 里至关重要。

## Send 与 Sync 是什么

`Send` 和 `Sync` 是 Rust 并发安全的核心——它们是编译期的 marker traits，标记一个类型是否可以跨线程使用。

**`Send`**: 这个类型的**所有权**可以在线程间转移。如果一个类型的所有字段都是 `Send`，它就是 `Send`——编译器自动推导。`Rc<T>` 不是 `Send`——它的引用计数操作不是原子的，移入另一个线程可能导致计数错误。

**`Sync`**: 这个类型可以通过**共享引用**（`&T`）在线程间访问。形式化定义：`T: Sync` 当且仅当 `&T: Send`。`RefCell<T>` 不是 `Sync`——它的运行时 borrow check 不保证线程安全。`Mutex<T>` 是 `Sync`（当 `T: Send`）——Mutex 用锁保护内部，允许多线程通过 `&Mutex<T>` 安全访问。

## 编译器如何推导

编译器检查类型的每个字段：如果所有字段都是 `Send`，类型自动是 `Send`。如果所有字段都是 `Sync`，类型自动是 `Sync`。只有引入**裸指针**（`*const T`, `*mut T`）或**非线程安全原语**（`Rc`, `RefCell`, `Cell`, `UnsafeCell`）时，类型才会失去这些属性。

```rust
fn assert_send<T: Send>() {}
assert_send::<i32>();                // OK
assert_send::<Arc<String>>();        // OK (Arc 用原子操作同步)
// assert_send::<Rc<String>>();      // COMPILE ERROR
// assert_send::<*const i32>();      // COMPILE ERROR — 裸指针没有所有权语义

fn assert_sync<T: Sync>() {}
assert_sync::<Mutex<i32>>();         // OK (Mutex 保证内部同步)
// assert_sync::<RefCell<i32>>();    // COMPILE ERROR
```

## PhantomData 用于手动控制

用裸指针实现自定义容器时，裸指针不携带 Send/Sync——但你的容器逻辑上可能有这些保证：

```rust
use std::marker::PhantomData;
use std::rc::Rc;

struct MyNonSend {
    data: i32,
    _not_send: PhantomData<Rc<()>>,  // 标记: 这个类型不 Send (继承了 Rc 的属性)
}
// 虽然 MyNonSend 内部只有 i32(是 Send), 但 PhantomData<Rc<()>> 让它变成 !Send
```

PhantomData 不占内存（ZST），只在类型层面做标记。unsafe 代码的作者需要用 PhantomData 向编译器传达"这个类型应该/不应该 Send/Sync"——然后编译器会为调用方做正确性检查。

## 为什么不是"所有类型默认 Send"

标准库的类型经过精心设计：`Rc<T>` 天然不 Send，`Mutex<T>` 天然 Sync。第三方库中任何引入内部可变性的类型都需要仔细考虑 Send/Sync 的传播。如果一个类型意外地变成了 Send 而实际上不是线程安全的——那会是数据竞争的后门。这种"自动推导 + 人能介入"的设计让 Rust 在性能（零开销）和安全（编译期保证）之间取得平衡。

## 参考

- **Rustonomicon**: Send and Sync
- **Rust Reference**: auto traits

*Keywords: Send, Sync, auto trait, PhantomData, thread safety, marker type, Rc, RefCell*
