Rust 安全代码
安全与非安全代码对比 安全与非安全代码交互 非安全Rust能做什么 编写非安全代码数据布局
repr(Rust) 类型中的奇行种 可选的数据表达方式所有权
所有权 引用 别名 生命周期 生命周期的局限 省略生命周期 无界生命周期 高阶trait边界 子类型和变性 Drop检查 幽灵数据 分解借用类型转换
类型转换 强制类型转换 点操作符 显式类型转换 变形未初始化内存
未初始化内存 安全方式 Drop标志 非安全方式资源管理
基于所有权的资源管理 构造函数 析构函数 泄露展开
展开 异常安全性 污染并发
并发 竞争 Send和Sync 原子操作实现 Vec
实现 Vec 布局 内存分配 push和pop 回收资源 DeRef 插入和删除 IntoIter RawVec Drain 处理零尺寸类型 最终代码FFI
FFIRust 安全代码
安全与非安全代码对比 安全与非安全代码交互 非安全Rust能做什么 编写非安全代码数据布局
repr(Rust) 类型中的奇行种 可选的数据表达方式所有权
所有权 引用 别名 生命周期 生命周期的局限 省略生命周期 无界生命周期 高阶trait边界 子类型和变性 Drop检查 幽灵数据 分解借用类型转换
类型转换 强制类型转换 点操作符 显式类型转换 变形未初始化内存
未初始化内存 安全方式 Drop标志 非安全方式资源管理
基于所有权的资源管理 构造函数 析构函数 泄露展开
展开 异常安全性 污染并发
并发 竞争 Send和Sync 原子操作实现 Vec
实现 Vec 布局 内存分配 push和pop 回收资源 DeRef 插入和删除 IntoIter RawVec Drain 处理零尺寸类型 最终代码FFI
FFIRust 所有权
所有权是Rust的一个突破性功能。它让Rust可以彻底告别垃圾回收,同时做到内存安全和高效率。在涉及到所有权系统的细节之前,我们先看一下这种设计的目的。
我们假设你认同垃圾回收器(GC)不总是内存管理的最佳方案,在一些场景中需要手工地管理内存。如果你并不这么认为,那么请出门右转使用其他的语言吧。
但是,无论你怎么看待GC,它确实是保证代码安全的大杀器。你永远不需要担心有什么内容会被过早释放(尽管有的时候你已经不想再使用它们了……)。这是C和C++会普遍遇到的问题。看一下这个曾纠缠过每一个使用过非GC语言的人的简单错误:
fn as_str(data: &u32) -> &str {
// 计算字符串
let s = format!("{}", data);
// 哎呀!我们返回了一个只在函数内部存在的东西的引用
// 悬垂指针!释放后引用!指针别名!
// (当然这段代码在Rust中不能编译)
&s
}
这正是Rust的所有权系统要解决的问题。Rust知道&s生效的作用域,所以可以避免出现逃逸。不过这个例子太简单了,哪怕是C的编译器也可能捕捉到其中的错误。但是当代码量越来越大,指针来自四面八方的不同的函数时,事情就变得复杂了。C编译器最终会败下阵来,无法作出充分的逃逸分析来判断你的代码是否足够健壮。它能做的只有假设你的程序是正确的从而接受它。
这种事情永远不会发生在Rust的世界。Rust需要程序员向编译器保证自己代码的健壮性。
当然,Rust所有权系统要做的事有很多,不是仅仅验证引用不会超出被引用内容作用域这么简单。这是因为保证指针有效的条件比这个要复杂得多。以下面的代码为例。
let mut data = vec![1, 2, 3];
// 获得内部引用
let x = &data[0];
// push方法导致data的内部存储位置重新分配了
// 悬垂指针!释放后引用!指针别名!
// (当然这段代码在Rust中不能编译)
data.push(4);
println!("{}", x);
简单地分析作用域不足以防止这个bug,因为data在我们使用它的范围内确实是一直存在的。但是它在我们引用它的同时发生了变化。这就是为什么Rust要求引用的存在要锁定被引用内容和它的owner。
下一章:Rust 引用
有两种引用的类型:共享指针:&可变指针:&mut它们遵守以下的规则:引用的生命周期不能超过被引用内容可变引用不能存在别名(alias)就这些。这就是全部的引用模型。当然,我们可能需要定义一下别名(alias) ...
AI 中文社