Rust 入门 所有权系统
所有权
所有权是 Rust 最为与众不同的特性,它让 Rust 无需垃圾回收(garbage collector)即可保障内存安全。因此,理解 Rust 中所有权如何工作是十分重要的。
什么是所有权
- Rust 的核心特性就是所有权
- 所有程序在运行时都必须管理程序所使用的内存
- 有些语言采用垃圾回器来保证内存安全
- 还有一部分语言需要程序员来管理内存
- Rust 是如何管理内存的?
- Rust 通过所有权系统来管理内存
- 通过所有权系统管理内存, 编译器在编译时会根据一系列的规则进行检查
- 当程序运行时, 所有权系统不会减慢程序运行的速度
栈(Stack) 与 堆(Heap)
- 栈和堆都是代码在运行时可供使用的内存, 但是它们的结构不同
- 值是位于栈上还是堆上影响了程序运行的速度
- 堆与栈更大程度上影响了语言的行为以及为何必须做出这样的抉择
栈(Stack)
- 栈以放入值的顺序存储值并以相反顺序取出值
- 这也被称作 后进先出(last in, first out)
- 不能从中间也不能从底部插入或者取出数据
- 增加数据叫做 进栈(pushing onto the stack)
- 而移出数据叫做 出栈(popping off the stack)
- 栈中的所有数据都必须占用已知且固定的大小
堆(Heap)
- 在编译时大小未知或大小可能变化的数据, 要存储在堆上
- 向堆存入数据时你要请求一定大小的空间
- 操作系统在堆的某处找到一块足够大的空位, 把它标记为已使用, 并返回一个表示该位置地址的 指针(pointer) , 这个过程称作 在堆上分配内存(allocating on the heap), 简称为 分配(allocating)
- 将数据推入栈中并不被认为是分配, 因为指针的大小是已知并且固定的, 你可以将指针存储在栈上, 当需要实际数据时, 必须访问指针指向的堆空间
认识所有权
所有权系统解决的问题
- 跟踪代码的哪些部分正在使用堆中的哪些数据
- 最小化堆中的重复数据
- 清理堆上未使用的数据避免内存空间不足
- 了解堆栈有助于理解所有权系统
所有权规则
- Rust 中的每一个值都有一个被称为其 所有者(owner) 的变量
- 值在任一时刻有且只有一个所有者
- 当所有者超出 作用域(scope), 这个值将被丢弃
变量的作用域指的是变量的有效范围, 当变量超出这个范围后, 值不再有效
内存和分配
-
首先我们需要选择一种存储在堆上的数据类型作为我们的研究对象, String这个类型就非常合适
-
我们通过
String::from()
函数将一个字符串字面值转换为一个字符串变量, 此时操作系统会分配内存给我们的示例程序 -
当这个字符串变量离开有效作用域时, Rust 程序会主动释放这部分内存, 不需要手动管理
- C/C++需要手动释放内存:
- 如果忘了手动释放内存, 就会造成内存泄漏
- 如果提前释放了内存, 就会造成悬垂指针
- 如果释放了两次, 就会造成数据丢失或者其他问题
- Java 拥有GC垃圾回收器:
- GC会跟踪并识别内存, 当一段内存不再使用时, GC线程会主动释放内存
- C/C++需要手动释放内存:
-
在 Rust 中, 对于某个值来说, 当拥有此值的变量超出作用域时, 内存就会立刻释放
例子
fn main() {
let s1 = String::form("Hello"); // 给s1 赋值
let s2 = s1; // s1将所有权移交给了s2, 可以理解为将 s1 的值移动到 s2 中
// 此时如果我们使用s1这个变量, 就会报错
//println!("{}", s1);
// 如果我们需要两份相同的数据进行操作, 只能进行拷贝值
let s3 = s2.clone();
}
所有权与函数
- 将一个值传递给函数会发生移动或者复制
fn main() {
let i = 1;
func_1(i); // i32类型的存储位置是栈, 属于简单类型, 所以值被复制
let s = String::from("Hello");
func_2(s); // String类型的储存位置是堆, 属于复杂类型, 所以值被移动, 也就是所有权发生了转移
}
fn func_1(value: i32) {
println!("{}", value);
}
fn func_1(value: String) {
println!("{}", value);
}
- 函数在返回一个复杂类型的值时, 也会发生所有权的转移
fn main() {
let value = get_value();
println!("{}", value);
// value的所有权被移动到了func函数内
// func函数将value的所有权移动给了返回值
// 返回值的所有权被移动给了value2
let value2 = func(value);
println!("{}", value); // 此行报错, value的值被移动到了value2中, 也就是所有权发生了转移
println!("{}", value2);
}
fn get_value() -> String {
let value = String::from("Hello");
return value;
}
fn func(value: String) -> String {
return value;
}
- 把一个值赋给其他变量时, 所有权会发生转移
- 当一个包含 堆空间 数据的变量离开作用域时, 他的值会被drop函数清理, 除非所有权发生移动
如果对我分享的内容感兴趣的话 记得关注我得动态
求推荐 求收藏 求转发 求关注
求推荐 求收藏 求转发 求关注