C++ Rust异同
是的,而且比「默认」更进一步:在安全 Rust 里它是唯一的资源管理方式,由编译器强制执行。
C++ 里 RAII 是一种你可以选择遵守的惯用法,你仍然可以写 new 和 delete、持有裸指针。Rust 把它做成了语言规则:每个值有且只有一个所有者,所有者离开作用域时值被自动释放。
// 作用域结束:g、v、f 按声明的逆序依次释放,不用写任何代码
析构逻辑写在 Drop trait 里,相当于 C++ 的析构函数。上面这段没有在机器上跑过,只是示意。
和 C++ 的 RAII 有什么不同
相同的是思想,不同的是有几处规则更严,也有几处你的 C++ 直觉会出错。
| 方面 | C++ | Rust |
|---|---|---|
| 是否强制 | 惯用法,可以绕开 | 语言规则,安全代码里绕不开 |
| 赋值和传参 | 默认拷贝,移动要写 std::move |
默认移动,拷贝要显式写 .clone() |
| 移动之后的源对象 | 仍然存在,处于「有效但未指定」状态,析构函数照常执行 | 编译器禁止再使用,也不会再对它调用析构 |
| 移动的实现 | 调用移动构造函数,可以自定义 | 按位拷贝,不可自定义,不会失败 |
| 构造 | 构造函数,失败靠抛异常 | 没有构造函数,用普通函数返回值,失败返回 Result |
| 手动释放 | 可以显式调用析构函数 | 不能直接调 drop 方法,只能用 drop(x) 把值移走 |
| 成员的析构顺序 | 按声明的逆序 | 按声明的顺序 |
| 局部变量的析构顺序 | 按声明的逆序 | 按声明的逆序,这一点相同 |
三个值得记住的点
一,移动后不析构。 这是和 C++ 最大的区别。C++ 的移动构造函数必须把源对象置成一个可以安全析构的空状态,比如把指针置空。Rust 不需要:编译器静态地知道这个值已经被移走了,直接不生成析构调用。所以 Rust 里不存在「被移走的空对象」这个概念,也不需要写移动构造函数。
二,锁守卫比 C++ 的更强。 C++ 的 std::lock_guard 只是约定:你可以忘记加锁就去访问数据。Rust 的 Mutex<T> 把数据包在锁里面,只有通过 lock() 返回的守卫才能拿到数据的引用,守卫析构时自动解锁。不加锁就访问数据,编译不过。
三,析构不保证一定执行。 这是 Rust 比你预期宽松的地方。std::mem::forget 可以让一个值不被析构,Rc 的循环引用也会导致泄漏,而且这些都算安全代码。Rust 保证的是不会出现释放后使用和重复释放,不保证不泄漏。所以不能把内存安全建立在「某个析构函数一定会跑」这个假设上。
面试时一句话
Rust 的所有权就是编译器强制执行的 RAII 加上默认移动语义。C++ 靠程序员自觉遵守的规则,Rust 靠编译器检查。
《Programming Rust》第 4 章和第 13 章讲的就是这部分,会把上面每一条和 C++ 做对比。
暂无评论,欢迎留下第一条评论。