Rust 之 Send 和 Sync 详解

"深入理解Rust并发安全的基石:Send和Sync trait"

Posted by Vlor on November 24, 2025

概述

Send和Sync是Rust语言中保证并发安全的两个核心标记trait,它们在编译期就能防止数据竞争,是Rust无数据竞争并发编程的基石。

核心概念

什么是Send和Sync?

  • Send: 表示类型的值可以安全地跨线程传递所有权
  • Sync: 表示类型的引用可以安全地在多个线程间共享
  • 标记trait: 不包含任何方法,只用于编译期安全检查

数据竞争防护

Rust通过所有权系统结合Send/Sync,在编译期就杜绝了以下情况:

  • 两个线程同时写入同一数据
  • 一个线程写入数据时另一个线程读取
  • 缺乏同步机制的并发访问

Send Trait详解

定义与作用

Send trait表明类型的实例所有权可以安全地转移到另一个线程。

pub unsafe auto trait Send {}

实现Send的类型

// 基本类型都是Send
let x: i32 = 42;        // Send ✓
let s: String = "hello".to_string();  // Send ✓
let v: Vec<i32> = vec![1, 2, 3];      // Send ✓

// 包含Send类型的结构体自动成为Send
struct MyStruct {
    data: i32,
    name: String,
} // 自动实现Send ✓

非Send类型示例

use std::rc::Rc;

let rc = Rc::new(42);
// Rc<i32>不是Send,以下代码无法编译:
// std::thread::spawn(move || {
//     println!("{}", rc);
// });

常见非Send类型:

  • Rc - 非原子引用计数
  • *const T, *mut T - 裸指针
  • RefCell - 非线程安全的内部可变性

Sync Trait详解

定义与作用

Sync trait表明类型的不可变引用(&T)可以安全地在多个线程间共享。

pub unsafe auto trait Sync {}

实现Sync的类型

// 基本类型的不可变引用都是Sync
let x: &i32 = &42;           // Sync ✓
let s: &String = &"hello".to_string(); // Sync ✓

// 同步原语
use std::sync::Mutex;
let lock: Mutex<i32> = Mutex::new(0); // Sync ✓

非Sync类型示例

use std::cell::RefCell;

let cell = RefCell::new(42);
// &RefCell<i32>不是Sync,以下代码无法编译:
// std::thread::spawn(move || {
//     *cell.borrow_mut() = 100;
// });

常见非Sync类型:

  • Rc - 非原子引用计数
  • *const T, *mut T - 裸指针
  • Cell, RefCell - 非线程安全的内部可变性

自动推导机制

编译器自动实现

Rust编译器会自动为复合类型实现Send和Sync:

// 自动实现Send的例子
struct Point {
    x: i32,
    y: i32,
} // 自动实现Send和Sync ✓

// 包含非Send类型导致整体非Send
struct NotSend {
    data: Rc<i32>,
} // 自动实现!Send和!Sync ✗

手动实现(不安全)

在极少数情况下需要手动实现:

use std::marker::PhantomData;

struct MyUnsafeType<T> {
    data: *mut T,
    _marker: PhantomData<T>,
}

// 不安全的手动实现
unsafe impl<T> Send for MyUnsafeType<T> {}
unsafe impl<T> Sync for MyUnsafeType<T> {}

实践应用

多线程编程

use std::thread;
use std::sync::{Arc, Mutex};

fn main() {
    // Arc是Send + Sync,可以安全跨线程共享
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}
类型 Send Sync 说明
i32, f64, bool 基本类型
String, Vec 集合类型
Rc 非原子引用计数
Arc 原子引用计数
Mutex 互斥锁
Cell 线程内部可变性
RefCell 非线程安全的内部可变性
MutexGuard<’a, T> 锁守卫,可转移但不能共享
&mut T 可变引用(当T是Send)
Raw pointer (*const T, *mut T) 裸指针

常见错误解决

// 错误:Rc不能跨线程
// let rc = Rc::new(5);
// thread::spawn(move || { println!("{}", rc); });

// 解决方案:使用Arc代替Rc
use std::sync::Arc;
let arc = Arc::new(5);
thread::spawn(move || { println!("{}", arc); });

总结要点

  1. Send关注所有权转移:能否安全地将值移动到另一个线程
  2. Sync关注引用共享:能否安全地在多个线程间共享不可变引用
  3. 自动推导:编译器自动为复合类型实现,基于字段类型
  4. 编译期检查:所有并发安全问题在编译时发现
  5. 安全抽象:通过标准库类型(Arc、Mutex等)构建安全并发程序

进阶资源

Rust社区风采

https://example.com/rust-community.jpg Rust社区致力于构建安全、并发的系统编程未来