Rust基础02-所有权规则、内存与分配
所有权规则
- 每个值都有一个变量,这个变量是该值的所有者
- 每个值同时只能有一个所有者
- 当所有者超出作用域(scope)时,该值将被删除
变量作用域
Scope 就是程序中一个项目的有效范围
fn main() {
//s 不可用
let s = "hello";//s 可用
//可对 s 进行相关操作
} //s 作用域到此结束,s 不再可用
String 类型
字符串字面值:程序里手写的那些字符串,它们是不可变的
String类型:在Heap上分配,能够存储在编译时未知数量的文本
-
创建 String类型 值
可以使用 from 函数从字符串字面值创建出 String 类型
fn main() { let s =String::from("hello"); }
其中,"::" 表示 from 是 String 类型下的函数,这类字符串是可以被修改的
String类型能够修改,字符串字面量不能修改,这是因为它们的存储方式不同
内存和分配
-
字符串字面值:
在编译时就知道它的内容,其文本内容直接被硬编码到最终的可执行文件中
速度快、高效、不可变性
-
String 类型:
支持可变性,在Heap上分配内存来保存编译时未知的文本内容
-
操作系统必须在运行时请求内存
此步通过调用 String::from 来实现
-
当用完 String 后,需要使用某种方式将内存返回给操作系统
但Rust采用了不同的方式:对于某个值来说,当拥有它的变量走出作用范围时,内存会立即自动的交还给操作系统
当变量走出作用域,Rust会自动调用 drop 函数
-
-
移动(Move):
-
普通情况:
多个变量可以于同一个数据使用一种独特的方式来交互
let x = 5; let y = x;
整数是已知且固定大小的简单的值,这两个5被压入Stack中
-
String 版本:
let s1 = String::from("hello"); let s2 = s1;
一个String由三部分组成:
-
一个指向存放字符串内容的内存的指针
-
一个长度:
长度len,就是存放字符串内容所需的字节数
-
一个容量:
容量capacity,指String从操作系统总共获得内存的总字节数
以上三部分放在 Stack 上,存放字符串内容的部分在 Heap 上
当把 s1 赋给 s2,String 的数据被复制了一份:
-
在 Stack 上复制了一份指针、长度、容量
-
并没有复制指针所指向的 Heap 上的数据
当变量离开作用域时,Rust 会自动调用 drop 函数,并将变量使用的 Heap 内存释放
当s1、s2离开作用域时,它们都会尝试释放相同的内存:
这会引起严重的BUG:二次释放(double free)bug
因此,Rust为了保证内存安全:
-
Rust 没有尝试复制被分配的内存
-
Rust 让 s1 失效,当 s1 离开作用域时,Rust不需要释放任何东西
引申:浅拷贝与深拷贝
-
-
-
克隆(Clone):
若有意对 Heap 上的 String 数据进行深度拷贝,而不仅仅是 Stack 上的数据,可以使用 clone 方法
这种方法较为耗费资源且针对 Heap 上的数据进行操作
-
复制(Copy):对 Stack 上的数据进行操作
Copy trait,可以用于像整数这样完全存放在 Stack 上的类型
-
如果一个类型实现了 Copy 这个 trait,那么旧的变量在赋值后仍然可用
-
但若一个类型或该类型的一部分已实现了 Drop trait,那么 Rust 不允许让它再去实现 Copy trait 了
任何简单标量的组合类型都可以是 Copy 的,任何需要分配内存或某种资源的都不是 Copy 的
一些拥有 Copy trait 的类型:
- 所有的整数类型,例如 u32
- 布尔类型
- 字符类型
- 所有的浮点类型,例如 f64
- 元组,如果其所有的字段都是 Copy 的
-