深入理解Rust中的 Send 和 Sync trait

 更新时间:2025年11月24日 11:11:58   作者:alwaysrun  
Rust中的标记 trait(Marker Trait)Send和Sync是并发安全的核心基石,这篇文章给大家介绍Rust中的Send和Sync trait,感兴趣的朋友跟随小编一起看看吧

Rust中的标记 trait(Marker Trait)SendSync 是并发安全的核心基石。用于标记类型的并发安全属性,让编译器在编译时检查线程间数据传递的合法性,从根源上避免数据竞争(Data Race)。

  • Send:类型的值能不能在线程之间“移动所有权”。
  • Sync:类型的值能不能在多个线程中“共享引用 &T”。
    • 对同一个值进行并发访问(只读)是内存安全的。
    • 并发修改仍然要靠锁/原子/其他机制。

Send

一个类型 TSend,表示把 T 的一个 值 从一个线程移动到另一个线程,不会引发数据竞争或内存安全问题。

  • 移动的是 所有权,而不是引用。
  • 移动之后,原线程不能再用这个值(所有权已经被 move)。

绝大多数类型自动实现 Send,以下一些例外(!Send):

  • Rc<T>:引用计数是非原子操作,跨线程转移会导致计数竞争(数据竞争)。
  • RefCell<T>/Cell<T>:内部可变性无同步机制,跨线程转移后,多个线程可能同时修改数据。
  • 裸指针 *const T/*mut T:无安全保证,直接跨线程转移可能导致悬垂指针或数据竞争。
  • UnsafeCell<T>:内部可变性的底层实现,本身 !Send(但基于它的线程安全类型如 Mutex 会手动实现 Send)。
use std::thread;
fn main() {
    let v = vec![1, 2, 3];
    // Vec<i32> 是 Send,通过move把所有权移到线程中
    let handle = thread::spawn(move || {
        println!("{:?}", v);
    });
    handle.join().unwrap();
}

sync

一个类型 TSync,表示可以安全地被多个线程共享(即 &TSend)。即,如果一个类型的引用 &T 能安全跨线程传递,那么这个类型就是 Sync(多个线程持有 &T 不会导致数据竞争)。

任何允许通过 共享引用 &T 获得可变访问 且没有同步保护的类型,都不是 Sync

  • Cell<T> / RefCell<T>:允许通过 &T 改变内部数据(“内部可变性”),但不保证线程安全。
  • Rc<T>:通过共享引用可以克隆 Rc 得到更多引用,从而在多线程下导致竞争。
  • 非线程安全的 FFI 对象,如果通过 & 能调用会修改内部状态的方法。
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
    let data = Arc::new(Mutex::new(5)); // Arc<Mutex<i32>>
    let data_clone = data.clone();
    thread::spawn(move || {
        let mut guard = data_clone.lock().unwrap(); // 加锁,独占访问
        *guard += 1;
    }).join().unwrap();
    println!("{}", data.lock().unwrap()); // 输出 6
}

auto trait

SendSync 都是 auto trait,编译器会根据类型的组成部分自动推导实现。

性质含义常见例子
Send + Sync可以在线程间移动,也可以多线程共享引用i32, Mutex<T>
Send + !Sync可以在线程间移动,但不能多线程共享引用一些 *mut T封装,Sender<T>
!Send + Sync不能跨线程移动;不移动,只读共享是安全的(较少见)少数特殊 FFI 对象
!Send + !Sync既不能跨线程移动,也不能多线程共享引用Rc<RefCell<T>>

只有在特殊情况下(写 底层库 / FFI 封装 / 自己的锁和原子数据结构 时)才会需要手动实现SendSync

  • unsafe impl Send / unsafe impl Sync 是非常严肃的承诺
  • 要自己保证所有可能的并发访问路径都不会产生数据竞争、悬垂指针等。
use std::sync::Mutex;
// 自定义类型:用 Mutex 保护裸指针(裸指针本身 !Send/!Sync)
struct SafePtr<T>(Mutex<*mut T>);
impl<T> SafePtr<T> {
    fn new(value: T) -> Self {
        let ptr = Box::into_raw(Box::new(value)); // 裸指针指向堆内存
        SafePtr(Mutex::new(ptr))
    }
    // 安全访问:通过 Mutex 加锁,保证独占访问
    fn get(&self) -> Option<&T> {
        let guard = self.0.lock().ok()?;
        unsafe { (*guard).as_ref() } // 裸指针解引用需 unsafe,但锁保证安全
    }
}
// 手动实现 Send/Sync:因为内部用 Mutex 保护,访问裸指针是线程安全的
unsafe impl<T> Send for SafePtr<T> {}
unsafe impl<T> Sync for SafePtr<T> {}
// 测试:跨线程共享 SafePtr
fn main() {
    let ptr = SafePtr::new(5);
    let ptr_clone = Arc::new(ptr);
    let ptr_clone2 = ptr_clone.clone();
    thread::spawn(move || {
        println!("线程内访问:{}", ptr_clone2.get().unwrap()); // 安全
    }).join().unwrap();
}

到此这篇关于深入理解Rust中的 Send 和 Sync trait的文章就介绍到这了,更多相关Rust Send 和 Sync trait内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Rust语言的新手了解和学习入门启蒙教程

    Rust语言的新手了解和学习入门启蒙教程

    这篇文章主要介绍了rust的特点、安装、项目结构、IDE环境配置、代码运行,讲解了如何安装Rust编译器,创建和运行第一个Rust程序,并对Rust语言的特点和优势作了说明,包括内存安全、高效性能、并发性、社区支持和统一包管理等,是新手了解和学习Rust语言的启蒙教程
    2024-12-12
  • Rust用宏实现参数可变的函数的实现示例

    Rust用宏实现参数可变的函数的实现示例

    本文主要介绍了Rust用宏实现参数可变的函数的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • Rust中的Trait与Trait Bounds详解

    Rust中的Trait与Trait Bounds详解

    Rust中的Trait与TraitBounds通过《西游记》的故事背景进行解释,Trait是一种接口定义机制,用于描述角色的能力;TraitBounds用于限制函数或结构体的参数类型必须实现某些trait;BlanketImplementations可以为所有实现了某类trait的类型提供默认的trait实现
    2025-02-02
  • Rust指南枚举类与模式匹配详解

    Rust指南枚举类与模式匹配详解

    这篇文章主要介绍了Rust指南枚举类与模式匹配精讲,枚举允许我们列举所有可能的值来定义一个类型,枚举中的值也叫变体,今天通过一个例子给大家详细讲解,需要的朋友可以参考下
    2022-09-09
  • Rust duckdb和polars读csv文件比较情况

    Rust duckdb和polars读csv文件比较情况

    duckdb在数据分析上,有非常多不错的特质,1、快;2、客户体验好,特别是可以同时批量读csv在一个目录下的csv等文件,今天来比较下Rust duckdb和polars读csv文件比较的情况,感兴趣的朋友一起看看吧
    2024-06-06
  • 浅谈Rust中声明可见性

    浅谈Rust中声明可见性

    在Rust编程语言中,声明可见性是一个核心概念,本文主要介绍了Rust中声明可见性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • rust udp编程方法的具体使用

    rust udp编程方法的具体使用

    Rust标准库通过std::net模块提供了UDP网络编程支持,下面就来介绍一下rust udp编程方法的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2025-06-06
  • 详解Rust中#[derive]属性怎么使用

    详解Rust中#[derive]属性怎么使用

    在 Rust 中,#[derive] 是一个属性,用于自动为类型生成常见的实现,下面就跟随小编一起来学习一下Rust中derive属性的具体使用吧
    2024-11-11
  • 从零开始使用Rust编写nginx(TLS证书快过期了)

    从零开始使用Rust编写nginx(TLS证书快过期了)

    wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 负载均衡, 静态文件服务器,websocket代理,四层TCP/UDP转发,内网穿透等,本文给大家介绍从零开始使用Rust编写nginx(TLS证书快过期了),感兴趣的朋友一起看看吧
    2024-03-03
  • Rust常用功能实例代码汇总

    Rust常用功能实例代码汇总

    通过一系列实用示例展示了Rust在文件操作、网络请求、并发编程、命令行工具以及数据库操作等方面的应用,这些示例不仅展示了 Rust 的强大功能,还提供了实际开发中的指导和参考,通过这些示例,您可以更好地理解 Rust 的特性,并将其应用于您的项目中
    2024-12-12

最新评论