rust Send Sync 以及对象安全和对象不安全

发布于:2025-03-22 ⋅ 阅读:(10) ⋅ 点赞:(0)

开头:菜鸟小明的疑惑

小明:

“李哥,我最近学 Rust,感觉它超级严谨,啥 Send、Sync、对象安全、静态分发、动态分发的,我都搞晕了!为啥 Rust 要设计得这么复杂啊?”

小李(笑):

“别急,Rust 是因为想让代码‘安全’,又‘高性能’,所以才有这么多机制。

我们从头讲,慢慢来,一定搞清楚。”

---

第一章:线程安全是怎么做到的?Send / Sync

---

Send 是啥?

小明:

“我知道线程是并发执行的,但 Rust 的 Send 是干嘛的?”

小李:

“Send 就是让类型可以在线程之间安全传递,所有权移动。

如果一个类型实现了 Send,说明你可以把它交给另一个线程,不会有问题。”

let v = vec![1, 2, 3];

std::thread::spawn(move || {

    println!("{:?}", v); // v 安全移动到线程里

});

小李继续:

“像 Vec<T>、String 这些常见类型,默认都实现了 Send。”

---

Sync 又是啥?

小明:

“那 Sync 呢?”

小李:

“Sync 是说,多个线程可以安全共享一个类型的 引用 &T。

比如 &i32 是 Sync,多个线程读 &i32 没问题。”

小李举例:

“但如果是 Rc<T>,就不是 Sync,因为它没加锁,线程共享会炸!”

use std::rc::Rc;

let rc = Rc::new(5);

std::thread::spawn(move || {

    println!("{}", rc); // 编译报错!Rc 不是 Send

});

---

Rust 怎么防止线程不安全?

小明:

“那怎么防止?我不小心就传了个 Rc 进去怎么办?”

小李(笑):

“防不住啊?放心!Rust 的编译器帮你守着!

你跨线程传东西,Rust 会自动检查类型有没有 Send 和 Sync,

不符合就不给你编译过!

连运行都跑不起来,根本不可能线程不安全。”

---

第二章:对象安全是干嘛的?

---

什么是对象安全?

小明:

“线程安全我懂了。那对象安全呢?”

小李:

“对象安全是另一回事,管的是多态和动态分发。”

“Rust 里,想通过 dyn Trait 传递 trait 对象,trait 必须是对象安全的。

否则 Rust 编译器不让用。”

---

对象安全有啥用?

小李:

“对象安全让我们可以搞‘接口多态’,

比如你想写一个 draw 接口,不管是圆、方块、三角形,统统放一起画!”

trait Drawable {

    fn draw(&self);

}

fn render(shape: &dyn Drawable) {

    shape.draw();

}

“你能写 &dyn Drawable,是因为 Drawable 满足对象安全。”

---

什么叫不对象安全?

小明:

“那为啥有些 trait 不对象安全?”

小李:

“比如有个方法返回 Self,

dyn Trait 根本不知道 Self 是谁,咋办?

编译器说:‘我拒绝!’”

trait Factory {

    fn create() -> Self; // 返回 Self,不对象安全

}

---

对象安全的规则

小李总结:

1. 方法不能返回 Self(除非在 Box、Arc 里包起来)。

2. 不能有泛型方法。

3. 接口方法的 self 必须是 &self、&mut self 或 Box<Self>。

---

第三章:动态分发 VS 静态分发

---

动态分发(对象安全)

小李:

“dyn Trait 背后有个 vtable 指针,

运行时根据类型调用不同的方法。

这种叫动态分发,更灵活,但有运行时开销。”

---

静态分发(泛型、高性能)

小李继续:

“泛型和不对象安全的 trait,

Rust 编译器会为每个类型单独生成代码,

这种叫静态分发,零开销、性能好。”

---

第四章:Rust 为什么这么设计?

---

小明:

“李哥,我觉得 Rust 好麻烦,为什么要搞这么复杂?”

小李(认真):

“为了安全和性能!

C/C++ 时代:线程不安全、指针乱飞、内存泄露。

Rust 时代:编译时发现所有问题,安全有保障。

还能零开销,静态分发快得飞。”

---

第五章:总结表格

---

第六章:练习时间!

小李:

“明白了?来,练习一下!”

写个 trait Shape,加 fn area(&self) -> f64;,实现 dyn Shape 多态!

写个 trait Cloneable,fn clone_box(&self) -> Box<dyn Cloneable>,练习对象安全规则!

---

结尾

小明:

“李哥,真香!终于懂了 Rust 的对象安全和线程安全!”

“原来 Rust 是怕你写出有 bug 的代码,所以宁愿麻烦点,也要你写安全、性能高的代码!”

“学 Rust,心累但放心!