From 19d5c7fcb80923e9f1e8f42830f7dd2ab610d4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=A4=E7=8B=90=E4=B8=80=E5=86=B2?= <43949039+anonymousGiga@users.noreply.github.com> Date: Wed, 17 May 2023 14:30:34 +0800 Subject: [PATCH] Create chapter_3_7.md --- src/chapter_3/chapter_3_7.md | 142 +++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/chapter_3/chapter_3_7.md diff --git a/src/chapter_3/chapter_3_7.md b/src/chapter_3/chapter_3_7.md new file mode 100644 index 0000000..3822ebe --- /dev/null +++ b/src/chapter_3/chapter_3_7.md @@ -0,0 +1,142 @@ +## 3.7.所有权(Ownership) ---------- andy +### 3.7.1 所有权介绍 +所有权是Rust最为与众不同的特性,它让Rust无需垃圾回收即可保证内存安全。 + +#### 1. 所有权规则 + +Rust所有权的规则如下: + +- Rust中的每个值都有一个被称为其所有者的变量,即值的所有者是某个变量; +- 值在任何时刻有且仅有一个所有者; +- 当所有者离开作用域后,这个值将丢弃。 +```Rust +fn main() { + let a: u32 = 8; + let b: String = String::from("hello"); + let c: Vec = vec![1, 2, 3]; +} +``` + +上面的代码中,a就是8的所有者,b是String::from("hello")的所有者,c则是vec![1, 2, 3]的所有者。 + +注意:b是String::from("hello")的所有者,但是b不是字符串"hello"的所有者。同理,c是vec![1, 2, 3]的所有者,但不是[1, 2, 3]的所有者。至于为什么,后续内容(String类型部分)会进行讲解。 + +#### 2. 变量的作用域 +变量作用域是变量在程序中有效的范围。一对花括号表示的范围就是作用域,变量有效的范围就是从创建开始,到离开作用域结束。 +示例1: +```Rust +fn f() { + let b = 1u32; // --------------------------------| + let c = 2u32; //-----------| | + // | | + // | |---b的作用域范围 + println!("b = {:?}", b);// |--c的作用与范围 | + println!("c = {:?}", c);// | | + //-----------| -------------------| +} + +fn main() { + let a: u32 = 8; // ----------------------------| + println!("a = {:?}", a); // | + // | ---- a 的作用域范围 + f(); // | +//---------------------------------------------------| +} +``` + +示例2: +```Rust +fn main() { + let a = 8u32; // --------------------------| + { // | + let b = 5u32; // -------| | + println!("a = {:?}", a); // |--b的作用域范围 | + println!("b = {:?}", b); // | |-----a的作用域范围 + // -------| | + } // | + println!("a = {:?}", a); // | + // --------------------------| +} +``` + +#### 3. String类型 + +- String类型的创建 + + - String::from + - to_string + - String::new +```Rust +fn main() { + let s1 = String::from("Hello"); // 方法一 + let s2 = "Hello".to_string(); // 方法二 + + let mut s3 = String::new(); // 方法三 + s3.push('H'); + s3.push('e'); + s3.push('l'); + s3.push('l'); + s3.push('o'); + s3.push('!'); + + println!("s1: {:?}", s1); + println!("s2: {:?}", s2); + println!("s3: {:?}", s3); +} +``` + +- String类型的本质 + +Rust标准库中,String类型的定义如下: +```Rust +pub struct String { + vec: Vec, +} +``` +Vec类型的定义如下: +```Rust +pub struct Vec { + buf: RawVec, + len: usize, // 长度 +} +``` +RawVec定义则类似于如下(为了更好的说明String类型,下面的定义用简化的代码): +```Rust +struct RawVec { + ptr: NonNull, // 指针 + cap: usize, // 容量 +} +``` +那对于整个String类型,可以用伪代码表示如下: +```Rust +struct String { + v: struct Vec { + raw_vec: RawVec{ptr: NonNull, cap: usize}, + len: usize, + } +} +``` +更进一步的简化,可以得到String类型本质如下: +```Rust +struct String { + ptr:NonNull, + cap: usize, + len: usize, +} +``` +所以String类型本质是三个字段:一个指针,一个容量大小,一个长度大小。 + +- 内存分配 + +在Rust中,编译时大小确定的数据放在栈上,编译时大小不能确定的数据放在堆上。考虑如下代码: +```Rust +fn main() { + let mut s = String::new(); + s.push('A'); + s.push('B'); + + println!("{s}"); // 打印AB +} +``` +在第2行定义String类型时,并不能确定最终字符串的大小,所以字符串内容本身应该存储在堆上。结合什么String类型的本质的内容,可以得到String类型的存储如下: +![注释](../../assets/5.png)