一、什么是所有权
一旦理解了所有权,就不需要经常考虑栈和堆了。
一些语言自带垃圾回收机制
一些语言需要程序员手动分配内存和释放
Rust通过所有权系统管理内存,编译器会在编译时根据一些列规则进行检查。在运行时,所有权系统不会减慢程序。
栈(Stack):所有数据必须占用已知固定的大小
堆(Heap):
1. 大小未知或大小可能变化的数据存储在堆上
2. 往堆放入数据时,需要请求一定大小的空间,内存分配器会在堆的某处找到一块足够的空位,标记为已使用,并返回一个表示该位置地址的指针,这个过程叫做分配(allocating)。
入栈比堆分配快:因为入栈时分配器无需为存储新数据去搜索内存空间;且位置总是在栈顶。
访问堆数据比访问栈慢:因为必须通过指针来访问。现代处理器在内存中跳转越少就越快。
处理数据彼此较近建议用栈
处理数据彼此较远建议用堆
所有权系统解决的问题:
1. 跟踪哪部分代码正在使用堆上数据
2. 最大限度的减少堆上重复数据的数量
3. 清理堆上不再使用的数据确保不会耗尽空间
所有权规则:
1. Rust中每一个值都有一个被称为其所有者(Owner)的变量
2. 值在任一时刻有且只有一个所有者
3. 当所有者(变量)离开作用域,这个值将被抛弃
String类型:可变、可增长的文本片段,需要在堆上分配一块在编译时未知大小
Rust中内存在拥有它的变量离开作用域后就被自动释放
String 由三部分组成:一个指向存放字符串内容内存的指针,一个长度,和一个容量。这一组数据存储在栈上。右侧则是堆上存放内容的内存部分。
变量与数据交互的方式(一):移动
1. 二次释放问题。这个问题会导致内存污染。
2. Rust 禁止你使用无效的引用。
变量与数据交互的方式(二):克隆
哪些类型实现了Copy trait ?
- 所有整型:如u32、i32
- 布尔类型
* 所有浮点类型:如f64
* 字符类型,char
* 元组,tuple,当且仅当其包含类型也都实现Copy的时候
所有权与函数
Rust中,向函数传递值可能会移动或赋值,就像赋值语句一样。
变量的所有权总是遵循相同的模式:
将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 drop 被清理掉,除非数据被移动为另一个变量所有。
二、引用与借用
引用:(无所有权)
1. &符号,允许使用值但不获取其所有权
2. 与使用&引用相反的操作时解引用,使用解引用运算符*
借用:
1. 引用的一个行为
2. 在函数体里,被引用的变量无法修改自身引用的值
可变引用
1. 增加 &mut 标记引用即可改变引用的值
2. 限制:在同一时间只能有一个对某一特定数据的可变引用。(同一时间只能可变引用一次)
3. 上述限制的好处:在编译时避免数据竞争。
4. 不能在拥有不可变引用的同时拥有可变引用。
非词法作用域生命周期(NLL):
编译器在作用域结束之前判断不再使用的引用的能力。
悬垂引用:
指其指向的内存可能已经被分配给其他持有者
警告:最好不要将变量的引用通过函数返回值的方式返回
引用总结:
1. 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用。
2. 引用必须总是有效的。
三、字符串Slices(无所有权)
解决了什么问题?
A:数据从某个特定状态计算而来的值,与该状态完全没有关联,导致不相关变量需要保持同步。
let s = String::from("Hello");
let slice = &s[0..2] // -> He
// equally to
let slice = &s[..2] // -> He
let len = s.len();
let slice = &s[3..len];
// equally to
let slice = &s[3..];
字符串 Slice 类型声明标记:“&str”
code eg:
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}
fn main() {
let mut input = String::from("Hello World");
let word = first_word(&input); // 不可变引用
println!("first word is: {}", &word);
input.clear(); // 试图获取一个可变引用 input!
println!("sec word is: {}", &word); // 此时又访问了不可变引用,导致可变与不可变引用同时存在,非法!
}
字符串字面值就是Slice类型
1. 如:let s = "hello, world";
2. 此处s类型是 &str,由于 &str 是一个不可变引用,所以字符串字面值是不可变的。
其他类型的Slice
数组类型也有slice,工作方式与字符串的一样,同时存储第一个集合元素的引用和一个集合的总长度。
|
请发表评论