「Rust」2 所有权与借用
所有权
申请内存和释放空间主要有三种流派
- 垃圾回收机制(GC):在程序运行时不断寻找不再使用的内存, 如 Java、Go
- 手动管理内存的分配和释放, 在程序中,通过函数调用的方式来申请和释放内存,如 C++
- 通过所有权来管理内存,编译器在编译时会根据一系列规则进行检查
不安全代码
int* foo(){
int a;
a = 100;
char* c = "xyz";
return &a;
}
a会造成 悬空指针 的问题,c会占用内存回收较晚
栈(Stack)与堆(Heap)
栈
后进先出,栈中的所有数据都必须占用已知且固定大小的内存空间,假设数据大小是未知的,那么在取出数据时,你将无法取到你想要的数据。
堆
存储大小未知或可能变化的数据,操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的指针, 该过程被称为在堆上分配内存,有时简称为 「分配」(allocating)。接着,该指针会被推入栈中,因为指针的大小是已知且固定的,在后续使用过程中,你将通过栈中的指针,来获取数据在堆上的实际内存位置,进而访问该数据。
所有权原则
- Rust 中每一个值都
有且只有
一个所有者(变量) - 当所有者(变量)离开作用域范围时,这个值将被丢弃(free)
变量与作用域
从创建到离开作用域,和其他语言一样
变量绑定的数据交互
转移所有权
let x = 5;
let y = x;
let s1 = String::from("hello");
let s2 = s1;
在这里,由于5
是基本数据类型,两个值都是自动拷贝,存在栈中。
而String
是一个复杂类型,由存储在栈中的堆指针、字符串长度、字符串容量共同组成,所以赋值s2
后,s1
就失效了,相当于移动
克隆(深拷贝)
自动的复制都不是「深拷贝」,如果需要,可以使用clone
方法。
拷贝(浅拷贝)
任何基本类型的组合可以是
Copy
的,不需要分配内存或某种形式资源的类型是Copy
的
在这里,Copy
作为一个变量可以存在栈中的特征
- 所有整数类型,比如
u32
。 - 布尔类型,
bool
,它的值是true
和false
。 - 所有浮点数类型,比如
f64
。 - 字符类型,
char
。 - 元组,当且仅当其包含的类型也都是
Copy
的时候。比如,(i32, i32)
是Copy
的,但(i32, String)
就不是。
函数传值与返回
函数和变量名一样,可以拥有值的所有权,包括传入的参数和返回值
引用与借用
Rust通过借用(Borrowing)
这个概念,来获取变量的引用
引用与解引用
let x = 5;
let y = &x;//引用
assert_eq!(5,x);
assert_eq!(5,*y);//解引用
不可变引用与可变引用
fn main(){
let s = String::from("hello");
change(&s);//默认的引用也是不可变的
}
fn chnage(some_string: &String){
some_string.push_str(", world");
}
需要和变量一样加入&mut
fn main(){
let s = String::from("hello");
change(&mut s);//默认的引用也是不可变的
}
fn chnage(some_string: &mut String){
some_string.push_str(", world");
}
限制:
同一作用域,特定数据只能有一个可变引用
可以使用大括号限制可变引用作用范围
可变引用与不可变引用不能同时存在
引用作用域的结束位置从花括号变成最后一次使用的位置
Non-Lexical Lifetimes(NLL) 优化
悬垂引用(Dangling References)
fn dange()-> &String{
let s = String::from("hello");
&s
}//返回的&str是无效的
解决方法是直接返回String
,转移所有权