Rust 初学笔记 2
本篇概要
本篇着力去记录了Rust最核心的内存管理机制“所有权”,Rust中两种非常重要的数据类型Struct和enum。
3 Rust的所有权
3.1 初探所有权
-
Rust的核心功能之一:所有权
-
与内存管理方式息息相关:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。
-
所有权规则
所有权存在的意义,管理heap上的内存
- rust中的每一个值都有一个所有者
- 值在任一时刻有且只有一个所有者
- 当所有者离开作用域,这个值将被丢弃
-
变量作用域
进入作用域有效,出作用域无效
-
Rust的内存回收机制
{ let s =String::from ("hello"); //... } //当s出了其作用域的时候,自动调用内存处理机制,drop函数,将s进行销毁 //s字符串是存储在堆上的
- Rust永远不会自动进行深拷贝
fn main() { let s1 = String::from("hello"); let s2 = s1; println!("{}, world!", s1); } //当s1拷贝给s2的时候,s1自动丧失意义,因此代码会报错 //为什么s1无效,是因为为了放置二次释放的问题
- 深拷贝的方式,利用clone()
- 当clone出现的时候,代码可能相当耗费资源
- 但是当数据类型是整型的时候,拷贝的速度极快,因此这时候不再区分深浅拷贝,也就是和String的内存处理机制不同
fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 = {}, s2 = {}", s1, s2); }
- Rust 不允许自身或其任何部分实现了
Drop
trait 的类型使用Copy
trait。
-
所有权和函数
fn main() { let s = String::from("hello"); // s 进入作用域 takes_ownership(s); // s 的值移动到函数里 ... // ... 所以到这里不再有效 let x = 5; // x 进入作用域 makes_copy(x); // x 应该移动函数里, // 但 i32 是 Copy 的, // 所以在后面可继续使用 x } // 这里,x 先移出了作用域,然后是 s。但因为 s 的值已被移走, // 没有特殊之处 fn takes_ownership(some_string: String) { // some_string 进入作用域 println!("{}", some_string); } // 这里,some_string 移出作用域并调用 `drop` 方法。 // 占用的内存被释放 fn makes_copy(some_integer: i32) { // some_integer 进入作用域 println!("{}", some_integer); } // 这里,some_integer 移出作用域。没有特殊之处
-
返回值与作用域
fn main() { let s1 = gives_ownership(); // gives_ownership 将返回值 // 转移给 s1 let s2 = String::from("hello"); // s2 进入作用域 let s3 = takes_and_gives_back(s2); // s2 被移动到 // takes_and_gives_back 中, // 它也将返回值移给 s3 } // 这里,s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走, // 所以什么也不会发生。s1 离开作用域并被丢弃 fn gives_ownership() -> String { // gives_ownership 会将 // 返回值移动给 // 调用它的函数 let some_string = String::from("yours"); // some_string 进入作用域。 some_string // 返回 some_string // 并移出给调用的函数 // } // takes_and_gives_back 将传入字符串并返回该值 fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域 // a_string // 返回 a_string 并移出给调用的函数 }
变量的所有权总是遵循相同的模式,将值赋给另一个变量时移动它
可以使用元组来返回多个返回值
-
引用
不用获取所有权就可以使用值的功能
3.2 引用与借用
- 引用就是使用这个对象但是并不拥有对象的所有权。
- 将引用作为函数参数就叫做借用
- 一个作用域,只能有一个可变引用
- 一个作用域,不能同时拥有可变引用以及不可变引用(任意数量的不可变引用)
- 不可以发生悬空指针(悬空引用)
3.3 Slice类型
- 字符串切片
- 指向字符串部分一部分的引用
- format: [开始索引…结束索引] 结束索引应该大1
- 从0开始可以不写
- 结束索引是字符串结束,可以写s.len()
- […]指的是引用所有的
- &str 就是字符串切片,不可变的引用
- 数组切片
4 结构体数据类型
4.1 结构体的定义和实例化
-
自定义数据类型
最后一个变量后面也要有,
struct User{
username:String,
year_old:u6,
active:bool,
}
- 字符串赋值可以打乱顺序,但是都得赋值
- 字符串访问 user1.email
- 一个字段可变,所有字段可变
- 可以作为函数的返回值
- 字符串的简写:当字段名和形式参数的名字相同时候,可以直接不写后面的名字
- 可以基于某个struct来拆杆件一个新实例,就是把特殊的值进行赋值,其他的可以用 …structname进行赋值
4.1.1 Tuple struct
- 整体有名字,但是局部没有名字
struct color(i32,i32,i32);
struct point(i32,i32,i32);
4.1.2 Unit-Like struct
- 没有任何字段的struct
4.1.3 struct数据的所有权
- struct 里面可以放引用
- 但是引用需要配合生命周期使用
4.2 struct方法
将函数封装在struct里面,使用impl关键字进行封装。
struct Rectangle{
width:u32,
length:u32,
}
// 需要在impl块里面进行调用
impl Rectangle{
fn area(&self) -> u32{
self.width *self.length
}
}
fn main(){
let rect = Rectangle{
width = 30,
length = 15,
};
println!("{}",rect.area());
}
5 枚举和模式匹配
5.1 定义枚举
enum IPAddress{
V4,
V6,
}
let four = IP::V4;
let six = IP ::v6;
-
可以作为struct的字段
-
枚举类型可以嵌入任何类型的数据
枚举类型可以关联结构体
enum IPAddress{
V4(u8,u8,u8,u8),
V6(String),
}
5.1.1 枚举方法
可以使用impl进行枚举方法的定义
5.2 Option枚举
-
在预导入模块中
-
某个值可能存在可能不存在
-
Rust里面没有null
let absent_num : Option = None;
5.3 match
5.3.1 match方法的定义
// 起到一个匹配的作用
match coin{
Coin::Penny =>{
}
Coin::Nickel =>{
}
Coin::Dime =>{
}
}
5.3.2 绑定值的模式匹配
enum里面嵌套enum,但是使用一个并不是很明确的state来进行替代,这样最后就可以利用state了
5.3.3 match匹配的规则
- match匹配的时候必须穷举所有的可能,就是所有的模式都要有结论
- 剩下的可以用 _ ⇒ (),进行替代
5.4 if let
- 只针对一种模式进行匹配
- 放弃了穷举的可能
- if let {} else{}