diff --git a/src/chapter_2.md b/src/chapter_2.md index 8174d0d..b5df4ec 100644 --- a/src/chapter_2.md +++ b/src/chapter_2.md @@ -165,7 +165,7 @@ cargo new hello_world 默认情况下,main.rs文件中已经包含了一个简单的`Hello, World`程序,代码如下: -```Rust +```rust fn main() { println!("Hello, world!"); } diff --git a/src/chapter_3/chapter_3_1.md b/src/chapter_3/chapter_3_1.md index ca63ac2..d52267d 100644 --- a/src/chapter_3/chapter_3_1.md +++ b/src/chapter_3/chapter_3_1.md @@ -4,7 +4,7 @@ 在Rust中,将值和变量关联的过程称为绑定,变量的绑定可以使用`let`关键字,如下: -```Rust +```rust let a = 1; // 将1绑定到变量a let mut b = 2; // 将2绑定到变量b let some_number = Some(2); // 将Some(2)绑定到some_number @@ -16,7 +16,7 @@ Rust中变量分为不可变变量和可变变量。不可变变量不能对其 - 不可变变量定义方式如下: - ```Rust + ```rust let a: u32 = 1; //将1绑定到a这个变量 let b = 0u32; let c = 1; //定义时不指定类型,可以自动类型推导 @@ -24,14 +24,14 @@ Rust中变量分为不可变变量和可变变量。不可变变量不能对其 对不可变变量二次绑定一个值会报错: - ```Rust + ```rust let a: u32 = 1; //将1绑定到变量a,a为不可变变量, a = 2; //编译错误,a是不可变的变量,不能进行二次绑定 ``` - 可变变量定义方式如下: - ```Rust + ```rust let mut a: u32 = 1; //通过mut关键字定义可变变量 a = 2; //将2绑定到变量a,编译正确,因为a是可变变量,可以进行二次绑定 let mut b = 2; @@ -45,14 +45,14 @@ Rust中变量分为不可变变量和可变变量。不可变变量不能对其 常量是绑定到一个名称不允许改变的值,定义方式如下: -```Rust +```rust const HOUR_IN_SECONDS: u32 = 60 * 60; ``` 常量和不可变变量的区别: - 不允许对常量使用mut关键字,它总是不可变的,定义时必须显式的标注类型; - ```Rust + ```rust let a = 1u32; //编译正确 let a = 1; //编译正确 @@ -62,7 +62,7 @@ const HOUR_IN_SECONDS: u32 = 60 * 60; ``` - 常量可以在任何作用域声明,包括全局作用域; - 常量只能被设置为常量表达式,不能是在运行时计算出来的值。 - ```Rust + ```rust let a: u32 = 1; let b: u32 = 2; const A: u32 = a + b; //编译错误 @@ -72,7 +72,7 @@ const HOUR_IN_SECONDS: u32 = 60 * 60; Rust中可以定义一个与之前的变量同名的变量,这称之为第一个变量被第二个变量隐藏。隐藏和`mut`的区别:隐藏是定义了一个新的变量,而使用mut是修改原来的变量。 -```Rust +```rust fn main() { let a: u32 = 1; //这个变量a被下面的a隐藏掉了 let a: u32 = 2; //定义了一个新的变量,这个变量也叫作a diff --git a/src/chapter_3/chapter_3_10_1.md b/src/chapter_3/chapter_3_10_1.md index a156d77..a31d3f8 100644 --- a/src/chapter_3/chapter_3_10_1.md +++ b/src/chapter_3/chapter_3_10_1.md @@ -22,6 +22,7 @@ pub trait GetInformation { ### 2.1 为类型实现trait 代码示例如下: + ```rust // 定义trait pub trait GetInformation { @@ -33,6 +34,7 @@ pub struct Student { pub name: String, pub age: u32, } + // 为Student类型实现GetInformation trait impl GetInformation for Student { fn get_name(&self) -> &String { @@ -47,6 +49,7 @@ pub struct Teacher { pub name: String, pub age: u32, } + // 为Teacher类型实现GetInformation trait impl GetInformation for Teacher { fn get_name(&self) -> &String { @@ -77,6 +80,7 @@ fn main() { ### 2.2 可以在trait定义时提供默认实现 可以在定义trait的时候提供默认的行为,trait的类型可以使用默认的行为,示例如下: + ```rust // 定义trait pub trait GetInformation { @@ -90,6 +94,7 @@ pub struct Student { pub name: String, pub age: u32, } + // 为Student类型实现GetInformation trait impl GetInformation for Student { fn get_name(&self) -> &String { @@ -105,6 +110,7 @@ pub struct Teacher { pub name: String, pub age: u32, } + // 为Teacher类型实现GetInformation trait impl GetInformation for Teacher { fn get_name(&self) -> &String { @@ -155,6 +161,7 @@ pub struct Student { pub name: String, pub age: u32, } + // 为Student类型实现GetInformation trait impl GetInformation for Student { fn get_name(&self) -> &String { @@ -169,6 +176,7 @@ pub struct Teacher { pub name: String, pub age: u32, } + // 为Teacher类型实现GetInformation trait impl GetInformation for Teacher { fn get_name(&self) -> &String { @@ -203,6 +211,7 @@ fn main() { 上面中的print_information函数还可以写成如下: + ```rust // 使用trait bound的写法一 pub fn print_information(item: T) { @@ -212,6 +221,7 @@ pub fn print_information(item: T) { ``` 这种写法叫做Trait bound语法,它是Rust中用于指定泛型类型参数所需的trait的一种方式,它还可以使用where关键字写成如下: + ```rust // 使用trait bound的写法二 pub fn print_information(item: T) @@ -230,6 +240,7 @@ where pub trait GetName { fn get_name(&self) -> &String; } + pub trait GetAge { fn get_age(&self) -> u32; } @@ -328,6 +339,7 @@ impl GetName for Student { &self.name } } + struct Teacher { name: String, } @@ -362,7 +374,7 @@ fn main() { 错误原因分析(非常重要): 上面的代码中的`produce_item_with_name`函数的定义实际上等价于如下: -```rust +```Rust pub fn produce_item_with_name(is_teacher: bool) -> T { ... @@ -405,17 +417,19 @@ pub fn produce_item_with_name(is_teacher: bool) -> Student { 通过使用带有 `trait bound `的泛型参数的`impl` 块,可以有条件地只为那些实现了特定 trait 的类型实现方法,示例如下: ```rust - pub trait GetName { fn get_name(&self) -> &String; } + pub trait GetAge { fn get_age(&self) -> u32; } + struct PeopleMatchInformation { master: T, employee: U, } + // 11-15行也可以写成: impl PeopleMatchInformation impl PeopleMatchInformation where @@ -429,35 +443,42 @@ where println!("student age = {}", self.employee.get_age()); } } + //使用 pub struct Teacher { pub name: String, pub age: u32, } + impl GetName for Teacher { fn get_name(&self) -> &String { &(self.name) } } + impl GetAge for Teacher { fn get_age(&self) -> u32 { self.age } } + pub struct Student { pub name: String, pub age: u32, } + impl GetName for Student { fn get_name(&self) -> &String { &(self.name) } } + impl GetAge for Student { fn get_age(&self) -> u32 { self.age } } + fn main() { let t = Teacher { name: String::from("andy"), @@ -486,24 +507,29 @@ fn main() { pub trait GetName { fn get_name(&self) -> &String; } + pub trait PrintName { fn print_name(&self); } + // 为实现了GetName trait的类型实现PrintName trait impl PrintName for T { fn print_name(&self) { println!("name = {}", self.get_name()); } } + // 将为Student实现对应的trait pub struct Student { pub name: String, } + impl GetName for Student { fn get_name(&self) -> &String { &(self.name) } } + fn main() { let s = Student { name: String::from("Andy"), diff --git a/src/chapter_3/chapter_3_10_2.md b/src/chapter_3/chapter_3_10_2.md index 5f88ff1..f82cea9 100644 --- a/src/chapter_3/chapter_3_10_2.md +++ b/src/chapter_3/chapter_3_10_2.md @@ -7,7 +7,8 @@ 在Rust中,trait自身不能当作数据类型来使用,但trait 对象可以当作数据类型使用。因此,可以将实现了`Trait A`的类型`B`、`C`、`D`当作`trait A`的trait对象来使用。使用trait对象时,基本都是以引用的方式使用,所以使用时通常是引用符号加`dyn`关键字(即`&dyn`)。 示例如下: -```Rust + +```rust trait GetName { fn get_name(&self); } @@ -77,7 +78,7 @@ fn main() { 下面通过一段代码来分析一下使用trait对象时内存的布局。代码如下: -```Rust +```rust trait Vehicle { fn run(&self); } @@ -135,7 +136,7 @@ fn main() { 如下代码编译会报错,因为`Clone`返回的是`Self`: -```Rust +```rust pub struct Screen { pub components: Vec>, } diff --git a/src/chapter_3/chapter_3_11.md b/src/chapter_3/chapter_3_11.md index 2619fc0..554c7c9 100644 --- a/src/chapter_3/chapter_3_11.md +++ b/src/chapter_3/chapter_3_11.md @@ -8,7 +8,7 @@ Rust中的生命周期是用来管理内存的一种机制。在Rust中,内存 生命周期的主要目的就是为了避免悬垂引用。考虑如下代码: -```Rust +```rust fn main() { let r; { @@ -28,7 +28,8 @@ fn main() { Rust编译器有一个借用检查器,用它来检查所有的应用的都是有效的,具体的方式为比较变量及其引用的作用域。 ### 1. 示例1如下: -```Rust + +```rust fn main() { let r; //------------------------+-------'a { // | @@ -39,12 +40,13 @@ fn main() { println!("r = {}", r); // | //r为悬垂引用 } //---------------------------------------+ ``` + 对于上面的代码,借用检查器将`r`的生命周期标注为`'a`,将x的生命周期标注为`'b`,然后比较`'a`和`'b`的范围,发现`'b < 'a`,被引用的对象比它的引用者存在的时间还短,然后编译报错。 ### 2. 示例2如下: -```Rust +```rust fn main() { let x = 5; // ----------+-- 'b // | @@ -54,13 +56,14 @@ fn main() { // --+ | } // ----------+ ``` + 对于上面的代码,借用检查器将x的生命周期标注为`'b`,将`r`的生命周期标注为`'a`,比较两者范围,发现`'b > 'a`,被引用对象比它的应用者存在的时间长,编译检查通过。 ## 3.11.3 编译器有时无法自动推导生命周期 如下代码会报错: -```Rust +```rust fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x @@ -97,7 +100,7 @@ fn main() { 对于3.11.3例子中的函数,显式标注生命周期后的代码如下: -```Rust +```rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x @@ -113,7 +116,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { (1)指定生命周期参数的正确方式依赖函数实现的具体功能。如下代码中将不用标注`y`的生命周期,因为返回值不依赖于y的生命周期。 -```Rust +```rust fn longest<'a>(x: &'a str, y: &str) -> &'a str { x } @@ -121,7 +124,7 @@ fn longest<'a>(x: &'a str, y: &str) -> &'a str { (2)当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。如果返回的引用没有指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值,它将会是一个悬垂引用。如下代码将编译错误: -```Rust +```rust fn longest<'a>(x: &str, y: &str) -> &'a str { let result = String::from("really long string"); result.as_str() //将产生悬垂引用,result在花括号前“}”离开作用域 @@ -132,7 +135,7 @@ fn longest<'a>(x: &str, y: &str) -> &'a str { 结构体中的生命周期标注示例如下: -```Rust +```rust #[derive(Debug)] struct A<'a> { name: &'a str, // 标注生命周期 @@ -149,7 +152,7 @@ fn main() { 在大多数情况下,程序员不用在代码中显式标注生命周期,因为编译器能自动推导。不标注生命周期,我们称之为生命周期省略。例如下面的代码可以正确编译: -```Rust +```rust fn get_s(s: &str) -> &str { s } @@ -170,6 +173,7 @@ a、每个引用的参数都有它自己的生命周期参数。例如如下: 两个引用参数的函数,则有两个生命周期 :`fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`以此类推。 b、如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数: + ```Rust fn foo(x: &i32) -> &i32 等价于 fn foo<'a>(x: &'a i32) -> &'a i32 ``` @@ -180,7 +184,7 @@ c、如果方法有多个输入生命周期参数,不过其中之一因为方 结构体字段的生命周期必须总是在impl关键字之后声明并在结构体名称之后使用,这些声明周期是结构体类型的一部分,示例如下: -```Rust +```rust struct StuA<'a> { name: &'a str, } @@ -193,7 +197,7 @@ impl<'a> StuA<'a> { 下面的例子中,其方法没有显式标注生命周期,因为它符合生命周期省略规则中的第三条规则,代码如下: -```Rust +```rust struct StuA<'a> { name: &'a str, } @@ -208,7 +212,7 @@ impl<'a> StuA<'a> { 静态生命周期定义方式为:`'static`,其生命周期存活于整个程序期间。所有的字符字面值都拥有`'static`生命周期,代码中可以如下来标注: -```Rust +```rust let s: &'static str = "I have a static filetime"; ``` @@ -216,7 +220,7 @@ let s: &'static str = "I have a static filetime"; 下面示例为在同一函数中指定泛型类型参数、trait bounds 和生命周期: -```Rust +```rust use std::fmt::Display; fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str where diff --git a/src/chapter_3/chapter_3_12.md b/src/chapter_3/chapter_3_12.md index f08d9c9..20723e3 100644 --- a/src/chapter_3/chapter_3_12.md +++ b/src/chapter_3/chapter_3_12.md @@ -7,7 +7,7 @@ Rust将错误分为两大类:可恢复的和不可恢复的错误。 ## 3.12.1 用panic!处理不可恢复错误 `panic!`的使用方式如下: -```Rust +```rust fn main() { panic!("crash and burn"); } @@ -27,7 +27,7 @@ fn main() { Rust中使用```Result```类型处理可恢复错误,其定义如下: -```Rust +```rust enum Result { Ok(T), Err(E), @@ -40,7 +40,7 @@ enum Result { 使用示例如下: -```Rust +```rust use std::fs::File; fn main() { let f = File::open("hello.txt"); @@ -57,7 +57,7 @@ fn main() { 第3行返回的结果就是一个```Result```类型,可以使用```match```匹配```Result```的具体类型。下面为使用```str```作为```Result```中的错误```E```的例子: -```Rust +```rust // 该函数返回结果为Result,其中T为(),E为具有静态生命周期的&str类型 fn produce_error(switch: bool) -> Result<(), &'static str> { if switch { @@ -86,7 +86,7 @@ fn main() { #### 3.1 使用unwrap简写: -```Rust +```rust use std::fs::File; fn main() { let f = File::open("hello.txt").unwrap(); //使用unwrap简写来获取到Result中的T类型, @@ -96,7 +96,7 @@ fn main() { #### 3.2 使用except简写: -```Rust +```rust use std::fs::File; fn main() { @@ -111,7 +111,7 @@ fn main() { 除了函数中处理错误外,还可以选择让调用者知道这个错误并决定如何处理,这叫做传播错误。示例如下: -```Rust +```rust fn produce_error(switch: bool) -> Result<(), &'static str> { if switch { return Err("This is a error"); @@ -145,7 +145,7 @@ fn main() { 传播错误可以用```?```进行简写,上面的```transmit_error```函数代码用简写方式示例如下: -```Rust +```rust fn produce_error(switch: bool) -> Result<(), &'static str> { if switch { return Err("This is a error"); @@ -175,7 +175,7 @@ fn main() { 下面是更复杂的简写: -```Rust +```rust use std::io; use std::io::Read; use std::fs::File; diff --git a/src/chapter_3/chapter_3_15_3.md b/src/chapter_3/chapter_3_15_3.md index b316835..0b429d3 100644 --- a/src/chapter_3/chapter_3_15_3.md +++ b/src/chapter_3/chapter_3_15_3.md @@ -8,7 +8,6 @@ Rust 中的 HashMap(哈希映射)是一个基于键值对的无序集合, 要创建一个新的空 HashMap,可以使用 `HashMap::new()` 方法。需要导入 `std::collections::HashMap` 模块以使用 HashMap。 ```rust - use std::collections::HashMap; let mut map = HashMap::new(); @@ -17,6 +16,7 @@ let mut map = HashMap::new(); ## 2. 插入键值对: 可以使用 `insert()` 方法向 HashMap 中添加键值对。如果使用相同的键插入新值,旧值将被替换。 + ```rust map.insert("one", 1); map.insert("two", 2); @@ -51,6 +51,7 @@ map.remove("one"); // 删除键为 "one" 的键值对 ## 6. 检查键是否存在: 可以使用 `contains_key()` 方法检查 HashMap 中是否存在指定的键。 + ```rust let has_key = map.contains_key("one"); // 返回布尔值 ``` @@ -58,6 +59,7 @@ let has_key = map.contains_key("one"); // 返回布尔值 ## 7. 更新值: 可以使用 `entry()` 方法与 `or_insert()` 方法结合,更新 HashMap 中的值或插入新值。 + ```rust *map.entry("three").or_insert(3) += 1; ``` diff --git a/src/chapter_3/chapter_3_15_5.md b/src/chapter_3/chapter_3_15_5.md index 04c146b..981d753 100644 --- a/src/chapter_3/chapter_3_15_5.md +++ b/src/chapter_3/chapter_3_15_5.md @@ -6,6 +6,7 @@ Rust 中的 LinkedList(链表)是一种线性数据结构,它由一系列 ## 1. 创建 LinkedList: 要创建一个新的空 LinkedList,可以使用 `LinkedList::new()` 方法。需要导入 `std::collections::LinkedList` 模块以使用 LinkedList。 + ```rust use std::collections::LinkedList; @@ -15,6 +16,7 @@ let mut list = LinkedList::new(); ## 2. 添加元素: 可以使用 `push_front()` 和 `push_back()` 方法将元素添加到链表的开头和结尾。 + ```rust list.push_front(1); list.push_back(2); diff --git a/src/chapter_3/chapter_3_16_2.md b/src/chapter_3/chapter_3_16_2.md index a4d3dae..a9227d4 100644 --- a/src/chapter_3/chapter_3_16_2.md +++ b/src/chapter_3/chapter_3_16_2.md @@ -5,7 +5,8 @@ ## 1. Box的基本使用方式 下面为```Box```使用的简单示例: -```Rust + +```rust fn main() { let b = Box::new(5); //此时5存储在堆上而不是栈上,b本身存储于栈上 println!("b = {}", b); //离开作用域时同时清楚堆和栈上的数据 @@ -27,7 +28,8 @@ fn main() { ### (1)场景1示例: 假定我们需要采用递归的方式定义一个```List```,其定义可能如下: -```Rust + +```rust // 下面的代码无法编译通过 use crate::List::{Nil, Cons}; enum List { @@ -48,7 +50,8 @@ fn main() { ![注释](.././assets/22.png) 此时就需要使用Box,其代码如下: -```Rust + +```rust use crate::List::{Nil, Cons}; enum List { Cons(i32, Box), // 用Box就把它变成了一个指针,Cons就类似于c语言的结构体定义: @@ -70,7 +73,8 @@ fn main() { 每个Box的大小是固定的,所以编译不会有问题。 ### (2)场景2示例: -```Rust + +```rust fn main() { let b = Box::new([100u32; 100]); println!("b = {:?}", b); @@ -82,7 +86,8 @@ fn main() { ``` ### (3)场景3示例: -```Rust + +```rust trait Vehicle { fn run(&self); } @@ -118,4 +123,3 @@ fn main() { vehicle_run(v2); } ``` - diff --git a/src/chapter_3/chapter_3_16_3.md b/src/chapter_3/chapter_3_16_3.md index 10f2a50..0ef65ed 100644 --- a/src/chapter_3/chapter_3_16_3.md +++ b/src/chapter_3/chapter_3_16_3.md @@ -4,7 +4,7 @@ 常规引用是一个指针类型,包含了目标数据存储的内存地址。对常规引用使用 `*` ,可以通过解引用的方式获取到内存地址对应的数据值,示例如下: -```Rust +```rust fn main() { let x = 5; let y = &x; @@ -18,7 +18,8 @@ fn main() { ## 2. 通过*使用智能指针背后的值 对于智能指针,也可以通过`*`使用其背后的值,示例如下: -```Rust + +```rust fn main() { let x = 5; let y = Box::new(x); @@ -31,15 +32,15 @@ fn main() { 实现`Deref trait`允许我们重载解引用运算符`*`。通过为类型实现`Deref trait`,类型可以被当做常规引用来对待。简单来说,如果类型A实现了`Deref trait`,那么就可以写如下代码: -```Rust - let a: A = A::new(); - let b = &a; - let c = *b; //对A实现了Deref trait,所以可以对A类型解引用 +```rust + let a: A = A::new(); + let b = &a; + let c = *b; //对A实现了Deref trait,所以可以对A类型解引用 ``` 下面的代码定义一个`MyBox`类型,并为其实现`Deref trait`: -```Rust +```rust use std::ops::Deref; struct MyBox(T); impl MyBox { @@ -67,7 +68,7 @@ fn main() { 对于函数和方法的传参,Rust 提供了隐式转换:Deref 转换。若一个类型实现了 Deref 特征,那它的引用在传给函数或方法时,会根据参数签名来决定是否进行隐式的 Deref 转换,例如: -```Rust +```rust use std::ops::Deref; struct MyString(String); impl Deref for MyString { // MyString类型实现了Deref trait @@ -95,7 +96,7 @@ fn main() { 下面为调用方法时发生的隐式自动转换的例子: -```Rust +```rust use std::ops::Deref; struct MyType(u32); impl MyType { @@ -125,7 +126,7 @@ fn main() { Deref还支持连续的隐式转换,示例如下: -```Rust +```rust fn print(s: &str) { println!("{}", s); } diff --git a/src/chapter_3/chapter_3_16_4.md b/src/chapter_3/chapter_3_16_4.md index 6a2fb1e..850b114 100644 --- a/src/chapter_3/chapter_3_16_4.md +++ b/src/chapter_3/chapter_3_16_4.md @@ -6,7 +6,7 @@ 为一个类型实现`Drop trait`的示例如下: -```Rust +```rust struct Dog(String); //下面为Dog实现Drop trait @@ -32,7 +32,7 @@ fn main() { 当要显示的清理值时,不能直接调用Drop trait里面的drop方法,而要使用`std::mem::drop`方法,示例如下: -```Rust +```rust struct Dog(String); //下面为Dog实现Drop trait diff --git a/src/chapter_3/chapter_3_16_5.md b/src/chapter_3/chapter_3_16_5.md index 4a2fe88..03b711f 100644 --- a/src/chapter_3/chapter_3_16_5.md +++ b/src/chapter_3/chapter_3_16_5.md @@ -8,7 +8,7 @@ 根据前面的知识,可能写出来的代码如下: -```Rust +```rust enum List { Cons(i32, Box), Nil, @@ -27,7 +27,7 @@ fn main() { Rc智能指针通过引用计数解决数据共享的问题,下面是Rc使用的简单代码: -```Rust +```rust use std::rc::Rc; fn main() { let a = Rc::new(5u32); @@ -42,7 +42,8 @@ fn main() { ![注释](.././assets/27.png) 前面共享列表的需求则可以使用Rc实现如下: -```Rust + +```rust enum List { Cons(i32, Rc), Nil, @@ -66,7 +67,7 @@ fn main() { 下面的示例打印了Rc的引用计数: -```Rust +```rust enum List { Cons(i32, Rc), Nil, diff --git a/src/chapter_3/chapter_3_16_6.md b/src/chapter_3/chapter_3_16_6.md index eeaa38b..48148cf 100644 --- a/src/chapter_3/chapter_3_16_6.md +++ b/src/chapter_3/chapter_3_16_6.md @@ -5,7 +5,7 @@ **内部可变性(Interior mutability)** 是Rust中的一个设计模式,它允许在有不可变引用时改变数据,这通常是借用规则所不允许的。RefCell正是为Rust提供内部可变性的智能指针。 在Rust中,当使用mut或者&mut显示的声明一个变量或者引用时,才能修改它们的值。编译器会对此严格检查。 -```Rust +```rust let mut a = 1u32; a = 2u32; // 可以修改a的值 let b = 3u32; @@ -14,7 +14,7 @@ b = 4u32; // 报错,不允许修改 但是当使用RefCell时,可以对其内部包含的内容进行修改,如下: -```Rust +```rust use std::cell::RefCell; fn main() { let data = RefCell::new(1); // data本身是不可变变量 @@ -32,7 +32,7 @@ fn main() { 下面是另一个使用RefCell的例子: -```Rust +```rust #[derive(Debug)] enum List { Cons(Rc>, Rc), @@ -61,7 +61,7 @@ fn main() { 下面是一个使用RefCell时容易犯错的例子: -```Rust +```rust use std::cell::RefCell; fn main() { let data = RefCell::new(1); // data本身是不可变变量 @@ -75,7 +75,7 @@ fn main() { > ***分析:此处需要注意的是,对于RefCell的可变引用、不可变引用的作用域范围(Rust 1.68.2中),其定义方式还是从定义开始,到花括号前结束。这和普通引用是不一样的,因为在新版编译器中(1.31以后),普通引用的作用域范围变成了从定义开始,到不再使用结束。因此,下面的代码是可以正确的***: -```Rust +```rust fn main() { let mut a = 5u32; let b = &mut a; // b是可变引用 diff --git a/src/chapter_3/chapter_3_16_7.md b/src/chapter_3/chapter_3_16_7.md index c6cd581..b315ab4 100644 --- a/src/chapter_3/chapter_3_16_7.md +++ b/src/chapter_3/chapter_3_16_7.md @@ -7,7 +7,7 @@ 下面的代码试图用之前学到的智能指针相关知识实现上面的链表: -```Rust +```rust use crate::List::{Cons, Nil}; use std::cell::RefCell; use std::rc::Rc; @@ -91,7 +91,7 @@ Weak类似于Rc,但它不持有所有权,它仅仅保存一份指向数据 对于上一节中循环链表的例子,使用Weak实现为如下: -```Rust +```rust use crate::List::{Cons, Nil}; use std::cell::RefCell; use std::rc::Rc; diff --git a/src/chapter_3/chapter_3_17_2.md b/src/chapter_3/chapter_3_17_2.md index 94a4fde..12ce52f 100644 --- a/src/chapter_3/chapter_3_17_2.md +++ b/src/chapter_3/chapter_3_17_2.md @@ -1,13 +1,13 @@ # 3.17.2 模块 (Module) ## 1. 使用模块对代码分组 使用模块方便对代码进行分组,以提高可读性和重用性,例如如下代码: -```Rust +```rust fn main() { println!("Produce something!"); } ``` 将其中的打印代码放入到一个模块中,变成如下: -```Rust +```rust mod factory { // 创建一个模块 pub fn produce() { // 将打印函数放在模块中 println!("Produce something!"); @@ -21,7 +21,7 @@ fn main() { ## 2. 定义模块控制作用域和私有性 使用模块可以控制作用域和私有性,示例如下: -```Rust +```rust mod factory { pub struct PubStruct { // 该结构体被定义为公有,外部可以使用 @@ -32,9 +32,9 @@ mod factory { // 该结构体定义为私有,外部无法使用 i: u32, } - + // 该函数被定义为公有,外部可以使用 - pub fn function1() { + pub fn function1() { let p1 = PubStruct { i: 3u32 }; println!("p1 = {:?}", p1.i); let p2 = PrivateStruct { i: 3u32 }; @@ -70,7 +70,7 @@ fn main() { - 绝对路径(absolute path):以 crate root开头的全路径;对于外部 crate 的代码,是以 crate 名开头的绝对路径,对于对于当前 crate 的代码,则以字面值 crate 开头。 - 相对路径(relative path):从当前模块开始,以 self、super 或当前模块的标识符开头。 -```Rust +```rust mod parent { pub struct A(pub u32); @@ -92,7 +92,7 @@ fn main() { ## 4. 使用use关键字引入作用域 在外部使用模块中的每个项都带上路径会显得比较重复,可以使用use关键字引入路径,示例如下: -```Rust +```rust mod parent { pub struct A(pub u32); @@ -123,7 +123,7 @@ fn main() { ``` 还可以使用as关键字为引入的项提供新的名字,示例如下: -```Rust +```rust pub mod factory { pub fn produce() { println!("Produce something!"); @@ -140,7 +140,7 @@ fn main() { 我们将上面第4点中的第二个例子拆成多个文件,步骤如下: - 创建一个factory.rs,其内容为mod factory中的内容: -```Rust +```rust // src/factory.rs pub fn produce() { println!("Produce something!"); @@ -148,7 +148,7 @@ pub fn produce() { ``` - 在main.rs中导出mod,如下: -```Rust +```rust // src/main.rs mod factory; // 导出factory module,module名字和文件名字同名 diff --git a/src/chapter_3/chapter_3_17_3.md b/src/chapter_3/chapter_3_17_3.md index 8e7bec1..627f16b 100644 --- a/src/chapter_3/chapter_3_17_3.md +++ b/src/chapter_3/chapter_3_17_3.md @@ -1,12 +1,14 @@ # 3.17.3 再谈crate ## 1. 创建二进制crate和库crate 可以被编译成可执行文件的crate就是二进制crate,它的代码中必定包含一个main函数。如下方式创建的就是一个二进制crate: -``` + +```bash cargo new main ``` 对于库crate的创建,需要加上--lib方式如下: -``` + +```bash cargo new my-lib --lib ``` @@ -17,12 +19,14 @@ cargo new my-lib --lib 下面以使用第三方crate rand为例,来进行演示。 ### (1)首先创建一个工程: -``` + +```bash cargo new main ``` ### (2)添加依赖: -``` + +```bash cd main ``` @@ -33,13 +37,13 @@ cd main ### (3)在代码中使用rand库 编写src/main.rs如下: -```Rust +```rust // src/main.rs use rand::prelude::*; // 引入rand库 fn main() { // 使用rand的函数 - let mut rng = rand::thread_rng(); + let mut rng = rand::thread_rng(); let y: f64 = rng.gen(); println!("y = {:?}", y); } diff --git a/src/chapter_3/chapter_3_17_4.md b/src/chapter_3/chapter_3_17_4.md index f43ba43..4df9ba1 100644 --- a/src/chapter_3/chapter_3_17_4.md +++ b/src/chapter_3/chapter_3_17_4.md @@ -1,23 +1,27 @@ # 3.17.4 工作空间 + **工作空间** 是一系列共享同样的 Cargo.lock 和输出目录的包。使用工作空间,可以将多个crate放在同一个目录下,通过共享依赖来提供更好的代码组织和构建支持。 假定有一个项目my-project,里面包含两个crate,分别是二进制crate main和库crate add,在crate main的代码中使用crate add的功能。下面的示例通过工作空间来组织和管理crate。 ## 1.创建整个工程 命令如下: -``` + +```bash mkdir my-project cd my-project ``` ## 2.创建crate add 命令如下: -``` + +```bash cargo new add --lib ``` ## 3.编写crate adder的代码 编辑add/src/lib.rs如下: -```Rust + +```rust // add/src/lib.rs pub fn add(left: u32, right: u32) -> u32 { left + right @@ -26,7 +30,8 @@ pub fn add(left: u32, right: u32) -> u32 { ## 4.创建crate main 命令如下: -``` + +```bash cargo new main ``` @@ -36,7 +41,7 @@ cargo new main # my-project/Cargo.toml [workspace] members = [ - "./main", + "./main", "./add", ] ``` @@ -57,7 +62,8 @@ add = { path = "../add" } # 添加这行:添加对crate add的依赖 ## 7.在crate main的代码中使用crate add的代码 编辑main/src/lib.rs如下: -```Rust + +```rust // main/src/lib.rs use add::*; fn main() { diff --git a/src/chapter_3/chapter_3_18_1.md b/src/chapter_3/chapter_3_18_1.md index 0d605fc..9e2c5df 100644 --- a/src/chapter_3/chapter_3_18_1.md +++ b/src/chapter_3/chapter_3_18_1.md @@ -1,5 +1,7 @@ # 3.18.1 编写测试 + ## 1. 测试初体验 + 测试函数体通常执行如下三种操作: - 设置需要的数据或者状态; - 运行需要测试的代码; @@ -9,14 +11,16 @@ Rust提供了专门用来编写测试的功能,即test属性、一些宏和sho Rust中的测试就是带有一个test属性注解的函数,使用Cargo创建一个新的库时,会自动生成一个测试模块和测试函数,这个模块就是一个编写测试的模板。下面进行演示: ### (1)创建一个库 运行如下命令生成一个库: -``` + +```bash cargo new adder --lib ``` ### (2)查看自动生成的测试模块 打开adder/src/lib.rs可以看到其中内容如下: -```Rust -pub fn add(left: usize, right: usize) -> usize { + +```rust +pub fn add(left: usize, right: usize) -> usize { left + right } @@ -25,7 +29,7 @@ pub fn add(left: usize, right: usize) -> usize { mod tests { use super::*; - #[test] + #[test] fn it_works() { // 这是一个测试函数,上面需要加上#[test] let result = add(2, 2); // 调用被测试的函数 assert_eq!(result, 4); // 用断言进行判断结果 @@ -36,7 +40,8 @@ mod tests { ### (3)运行测试 在adder目录下运行如下命令来执行测试函数: -``` + +```bash cargo test ``` @@ -50,7 +55,8 @@ cargo test - assert_ne!输入两个参数,两个参数不等,断言成功,否则断言失败; 几个断言的使用示例如下: -```Rust + +```rust pub fn add(left: usize, right: usize) -> usize { left + right } @@ -63,7 +69,7 @@ mod tests { fn it_works() { let result = add(2, 2); assert!(result==4); // 断言成功 - assert!(result==2); // 断言将失败 + assert!(result==2); // 断言将失败 assert_eq!(result, 4); // 断言成功 assert_eq!(result, 2); // 断言失败 assert_ne!(result, 2); // 断言成功 @@ -74,7 +80,8 @@ mod tests { ## 3. 使用should_panic! 可以使用should_panic!检查代码是否按照预期panic,示例如下: -```Rust + +```rust pub struct Guess { value: i32, } @@ -103,7 +110,8 @@ mod tests { ## 4. 使用Result 除了使用断言,还是可以使用Result类型。和断言不同的是,断言写在函数中,而Result类型则是作为测试函数的返回值,示例如下: -```Rust + +```rust #[cfg(test)] mod tests { #[test] diff --git a/src/chapter_3/chapter_3_18_2.md b/src/chapter_3/chapter_3_18_2.md index 84c52d6..101654d 100644 --- a/src/chapter_3/chapter_3_18_2.md +++ b/src/chapter_3/chapter_3_18_2.md @@ -5,14 +5,16 @@ ## 1. 并行或连续运行测试 当运行多个测试时,Rust默认使用线程并行运行。如果不希望测试并行运行,或者更加精确的控制线程的数量,可以传递 --test-threads参数来控制,示例如下: -``` + +```bash cargo test -- --test-threads=1 # 使用一个线程运行测试 ``` ## 2. 显示测试中的打印 有的时候,需要在运行测试时打印内容到标准输出,可以添加-- --nocapture或者-- --show-output,例如有如下代码: -```Rust + +```rust pub fn add(left: usize, right: usize) -> usize { left + right } @@ -30,18 +32,20 @@ mod tests { } ``` 要在运行测试时显示12行的打印,可以运行如下命令: -``` + +```bash cargo test -- --nocapture ``` 或者 -``` +```bash cargo test -- --show-output ``` ## 3. 运行单个测试 运行测试时,可以通过指定测试函数的名字来运行某个特定的测试函数。例如有如下测试代码: -```Rust + +```rust pub fn add_two(a: i32) -> i32 { a + 2 } @@ -66,8 +70,10 @@ mod tests { } } ``` + 通过指定函数名运行特定的测试函数,下面的命令只会运行函数add_two_and_two: -``` + +```bash cargo test add_two_and_two ``` @@ -78,7 +84,8 @@ cargo test add_two_and_two ## 4. 过滤运行测试 还可以指定部分测试的名称,任何名称匹配这个名称的测试会被运行。例如,因为上面的代码中,前两个测试的名称包含 add,则可以通过 cargo test add 来运行这两个测试: -``` + +```bash cargo test add ``` @@ -87,8 +94,10 @@ cargo test add ![注释](.././assets/60.png) ## 5. 忽略某个测试 + 有时候运行cargo test时想忽略其中的某个测试,此时可以通过使用ignore属性来标记该测试来排除它。例如有如下测试代码: -```Rust + +```rust pub fn add_two(a: i32) -> i32 { a + 2 } diff --git a/src/chapter_3/chapter_3_19.md b/src/chapter_3/chapter_3_19.md index 254f6c0..3b13c75 100644 --- a/src/chapter_3/chapter_3_19.md +++ b/src/chapter_3/chapter_3_19.md @@ -2,11 +2,11 @@ ## 3.19.1 包和模块级别的注释 包和模块的注释分为两种:行注释 //! 和块注释 /*! ... */ ,这些注释需要添加到文件的最上方。示例如下: -```Rust +```rust /*! -这是一个crate,里面有mod add,此处用的是块注释 +这是一个crate,里面有mod add,此处用的是块注释 */ -//! 此处再使用一下行注释 +//! 此处再使用一下行注释 mod add; pub mod sub; ``` @@ -16,9 +16,10 @@ pub mod sub; ![注释](.././assets/41.png) 而下面的示例因为没有将所有的包注释放在文件最上面,所以会报错: -```Rust + +```rust /*! -这是一个crate,里面有mod add,此处用的是块注释 +这是一个crate,里面有mod add,此处用的是块注释 */ pub mod add; @@ -28,9 +29,12 @@ pub mod sub; ## 3.19.2 文档测试 + ### 1. 文档测试 + Rust运行在文档注释中写测试用例,示例如下: -```Rust + +```rust /// `add` 将两个值相加 /// /// 下面是测试用例 @@ -47,8 +51,10 @@ pub fn add(left: u32, right: u32) -> u32 { 测试用例的内容用一对```包含,上面的代码在运行cargo test时,将会运行注释中的测试用例。 ### 2. 保留测试,隐藏注释 + 还可以保留文档测试的功能,但是把测试用例的内容在文档中隐藏起来,示例如下: -```Rust + +```rust /// `add` 将两个值相加 /// /// 下面是测试用例 @@ -105,7 +111,8 @@ pub fn sub(left: u32, right: u32) -> Option { ### 2. 文档搜索别名 可以在Rust文档中为类型定义搜索别名,以便更好的进行搜索,示例如下: -```Rust + +```rust #[doc(alias("x"))] pub struct A; ``` diff --git a/src/chapter_3/chapter_3_2.md b/src/chapter_3/chapter_3_2.md index ea247a7..aecbc0d 100644 --- a/src/chapter_3/chapter_3_2.md +++ b/src/chapter_3/chapter_3_2.md @@ -26,7 +26,7 @@ Rust中的整型和浮点型如下: (1)可以在数值字面量后面加上类型表示该类型的数值,如下: -```Rust +```rust fn main(){ let _a = 33u32; // 直接加类型后缀 let _b = 33_i32; // 使用_分隔数值和类型 @@ -37,7 +37,7 @@ fn main(){ (2)可以数值的任意位置使用下划线分割来增强可读性,如下: -```Rust +```rust fn main(){ let _a = 10_000_000u32; let _b = 1_234_3_u32; @@ -47,7 +47,7 @@ fn main(){ (3)当不明确指定变量的类型,也不明确指定数值字面量的类型后缀时,Rust默认将整数当做`i32`类型,将浮点数当做`f64`类型,如下: -```Rust +```rust fn main(){ let _a = 33; // 等价于 let _a: i32 = 33; 等价于 let _a = 33i32; let _b = 64.123; // 等价于let _b: f64 = 64.123; 等价于 let _b = 64.123f64; @@ -56,7 +56,7 @@ fn main(){ (4)Rust使用`0b`表示二进制、`0o`表示八进制、`0x`表示十六进制,如下: -```Rust +```rust fn main(){ let a: u32 = 0b101; // 二进制整数 let b: i32 = 0o17; // 八进制整数 @@ -68,7 +68,7 @@ fn main(){ (5)Rust中所有的数值类型都支持基本数学运算:加、减、乘、除、取余,如下: -```Rust +```rust fn main() { let sum = 5 + 10; let difference = 95.5 - 4.3; @@ -83,7 +83,7 @@ fn main() { Rust中的布尔型用`bool`表示,有两个可能的值,为`true`和`false`。布尔类型使用的场景主要是条件表达式(控制流的内容),使用如下: -```Rust +```rust fn main() { // 定义方式 let a: bool = true; @@ -108,7 +108,7 @@ fn main() { char类型用于存放单个unicode字符,占用4个字节空间。当存储char类型数据时,Rust会将其转换为utf-8编码的数据存储。`char`字面量是单引号包含的任意单个字符,字符类型使用示例如下: -```Rust +```rust fn main() { let c: char = 'z'; let x: char = 'x'; @@ -124,7 +124,7 @@ fn main() { 圆括号以及其中逗号分割的值列表组成元组,定义一个元组方式如下: -```Rust +```rust fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); } @@ -132,7 +132,7 @@ fn main() { 可以将元组重新结构到变量上,如下: -```Rust +```rust fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; @@ -142,7 +142,7 @@ fn main() { 也可以直接使用元组的元素,如下: -```Rust +```rust fn main() { let x: (i32, f64, u8) = (500, 6.4, 1); let first = x.0; @@ -153,7 +153,7 @@ fn main() { 不带任何值的元组,称为unit类型(单元元组),可以代表空值或者空的返回类型,如下: -```Rust +```rust fn main() { let x: () = (); // 将值()保存到类型为()的变量x中 } @@ -163,7 +163,7 @@ fn main() { 数组中的每个元素的类型必须相同,数组的长度是固定的,数组的定义方式如下: -```Rust +```rust fn main() { let a = [1, 2, 3, 4, 5]; //直接写数组的值 let b: [i32; 5] = [1, 2, 3, 4, 5]; //显示指定数组的类型和长度 @@ -173,7 +173,7 @@ fn main() { 数组通过索引来访问元素,索引从0开始计数,如下: -```Rust +```rust fn main() { let a = [1, 2, 3, 4, 5]; @@ -184,7 +184,7 @@ fn main() { Rust中,访问无效的索引元素会报错,如下: -```Rust +```rust fn main() { let a = [1, 2, 3, 4, 5]; let b = a[5]; // 错误,只能放为0-4,所以这个代码将无法编译 @@ -202,7 +202,7 @@ Rust中可以使用`as`进行类型转换。 - 可以使用`std::char::from_u32`将`u32`转换为`char`类型。 - 可以使用`std::char::from_digit`将十进制整型转换为`char`类型。 -```Rust +```rust fn main() { // 数值类型的转换 assert_eq!(10i8 as u16, 10u16); diff --git a/src/chapter_3/chapter_3_20.md b/src/chapter_3/chapter_3_20.md index 0acfe52..1639677 100644 --- a/src/chapter_3/chapter_3_20.md +++ b/src/chapter_3/chapter_3_20.md @@ -62,6 +62,7 @@ fn main() { ### 3.20.1.4. 线程与move闭包 move关键字可用于传递给thread::spawn的闭包,获取环境中的值的所有权,从而达到将值的所有权从一个线程传送到另一个线程的目的。示例如下: + ```rust use std::thread; fn main() { @@ -82,6 +83,7 @@ Rust中实现消息传递并发的主要工具是通道。通道由发送者和 - 接收者用来接收消息; - 发送者或者接收者任一被丢弃时就认为通道被关闭了。 Rust标准库中提供的通道叫做mpsc,是多个生产者,单个消费者的通道,其使用示例如下: + ```rust use std::sync::mpsc; use std::thread; @@ -107,6 +109,7 @@ fn main() { ### 3.20.2.2. 通道和所有权 在使用通道时,send 函数会获取参数的所有权并移动这个值归接收者所有。例如下面的代码将会编译错误: + ```rust use std::sync::mpsc; use std::thread; @@ -251,6 +254,7 @@ fn main() { ``` 因此要在多个线程间共享内存,还需要有一种机制能让多个线程都能访问Mutex保护的数据。根据本书目前学到的知识,可以想到Rc智能指针。上面的代码使用Rc智能指针后的示例如下: + ```rust use std::rc::Rc; use std::sync::Mutex; diff --git a/src/chapter_3/chapter_3_22_1.md b/src/chapter_3/chapter_3_22_1.md index 303832c..7a88ad9 100644 --- a/src/chapter_3/chapter_3_22_1.md +++ b/src/chapter_3/chapter_3_22_1.md @@ -5,7 +5,8 @@ build.rs中可以进行真正的项目代码编译前需要的额外的工作,例如在编译前为项目生成对应的文件、代码,编译所依赖的外部语言库等。build.rs放置在正式代码的外面(也就是src的外面)。 下面示例在build.rs中生成一个文件,然后在正式的项目代码中读取这个文件,build.rs中的代码如下: -```Rust + +```rust // build.rs use std::fs; @@ -16,7 +17,8 @@ fn main() -> std::io::Result<()> { ``` src/main.rs中的代码如下: -```Rust + +```rust // src/main.rs use std::fs; fn main() { @@ -43,10 +45,11 @@ fn main() { 在项目构建之前,Cargo会将build.rs编译成可执行文件,然后执行。在执行过程中,脚本可以使用println的方式跟Cargo进行通信,通信内容的格式为:cargo:真正的内容。 示例如下: -```Rust + +```rust // build.rs fn main() { - println!("cargo:rustc-link-search=/usr/local/lib/"); + println!("cargo:rustc-link-search=/usr/local/lib/"); println!("cargo:rustc-link-lib=dylib=pcre2-8"); println!("cargo:rerun-if-changed=src/lib.rs"); } @@ -68,6 +71,7 @@ fn main() { - 构建脚本的依赖 构建脚本也可以引入其它基于Cargo的依赖包,依赖方式为在Cargo.toml中添加依赖包,示例如下: + ```TOML # Cargo.toml @@ -87,13 +91,17 @@ cc = "1.0.46" # 可以在build.rs中使用cc相关的功能 ![注释](.././assets/52.png) c目录中的pass.c为c代码,源码如下: -```Rust + +```C // c/pass.c #include void set_err(char *message) { // 提供一个打印错误信息的函数 printf("err: %s\n", message); } +``` + +```Rust src目录中为Rust代码,源码如下: // ================== // 封装c函数 @@ -114,7 +122,7 @@ fn main() { build.rs中的内容如下: -```Rust +```rust // build.rs fn main() { // 以下代码告诉 Cargo ,`c/pass.c`发生改变,就重新运行当前的构建脚本 @@ -144,5 +152,3 @@ libc = "0.2.139" # src/main.rs中使用了libc这个库 ``` 可以使用cargo run对这个工程编译运行。 - - diff --git a/src/chapter_3/chapter_3_22_2.md b/src/chapter_3/chapter_3_22_2.md index 0443e48..0742830 100644 --- a/src/chapter_3/chapter_3_22_2.md +++ b/src/chapter_3/chapter_3_22_2.md @@ -13,7 +13,7 @@ 目录中的foo是Rust代码,用cargo new foo --lib创建,其中src/lib.rs的代码如下: -```Rust +```rust #![crate_type = "staticlib"] // 生成静态库 #[no_mangle] // 暴露给c的函数要声明#[no_mangle] pub extern fn foo(){ @@ -40,7 +40,7 @@ crate-type = ["staticlib"] # 表示编译为静态库 在foo目录下运行命令cargo build编译,在foo/target/debug/目录下会生成静态库libfoo.a。 main.c中的c代码如下: -```Rust +```C #include extern void foo(); // foo函数为Rust提供的函数 diff --git a/src/chapter_3/chapter_3_23_1.md b/src/chapter_3/chapter_3_23_1.md index 495367c..9e5c7b4 100644 --- a/src/chapter_3/chapter_3_23_1.md +++ b/src/chapter_3/chapter_3_23_1.md @@ -1,13 +1,15 @@ # 3.23.1 声明宏 声明宏使用macro_rules!定义,是Rust中最常用的宏形式。下面代码中定义Vec时使用的vec!就是一个声明宏: -```Rust + +```rust fn main() { let _v = vec![1, 2, 3]; // 使用声明宏vec!定义一个Vec } ``` 下面的例子演示定义一个声明宏并使用它: -```Rust + +```rust // 定义一个声明宏my_vec macro_rules! my_vec { ( $( $x:expr ),* ) => { diff --git a/src/chapter_3/chapter_3_23_2.md b/src/chapter_3/chapter_3_23_2.md index 132cd66..66a39f5 100644 --- a/src/chapter_3/chapter_3_23_2.md +++ b/src/chapter_3/chapter_3_23_2.md @@ -9,7 +9,8 @@ ## 1. 自定义derive宏 在本书之前就已经见到过的结构体上的#[derive(Copy, Debug, Clone)]就是自定义derive宏,其功能实际上就是为类型生成对应的代码。例如下面的代码: -```Rust + +```rust #[derive(Copy)] struct A { a: u32, @@ -36,7 +37,7 @@ struct A { ```TOML [workspace] members = [ - "./main", + "./main", "./impl-derive", "./my-trait", ] @@ -45,7 +46,8 @@ members = [ ### (1)实现my-trait - 运行cargo new my-trait --lib创建; - 编写src/lib.rs代码如下: -```Rust + +```rust // my-trait/src/lib.rs pub trait MyTrait{ // 定义MyTrait fn do_something(); @@ -68,7 +70,7 @@ edition = "2021" # 下面的几行为添加的内容 ####################### [lib] -proc-macro = true +proc-macro = true [dependencies] syn = "2.0.15" @@ -76,7 +78,7 @@ quote = "1.0.26" ``` - 编写src/lib.rs如下: -```Rust +```rust // impl-derive/src/lib.rs extern crate proc_macro; use crate::proc_macro::TokenStream; @@ -122,7 +124,7 @@ impl-derive = {path = "../impl-derive"} # 添加这行 ``` - 编写src/lib.rs: -```Rust +```rust // main/src/lib.rs use my_trait::MyTrait; use impl_derive::MyDeriveMacro; @@ -162,7 +164,7 @@ proc-macro = true # 添加这行 ``` - 编写impl-fn-macro/src/lib.rs如下: -```Rust +```rust // impl-fn-macro/src/lib.rs use proc_macro::TokenStream; @@ -182,7 +184,7 @@ impl-fn-macro = {path = "../impl-fn-macro"} # 添加这行 ``` - 编写main/src/main.rs代码如下: -```Rust +```rust // main/src/main.rs use impl_fn_macro::make_answer; make_answer!(); // 调用函数宏生成answer函数 @@ -216,7 +218,7 @@ impl-attr-macro中实现了类属性宏func_info。 ... ## 添加下面2行 -[lib] +[lib] proc_macro = true [dependencies] @@ -227,7 +229,7 @@ proc-macro2 = "1.0.56" ``` - 编辑impl-attr-macro/src/lib.rs文件如下: -```Rust +```rust use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, ItemFn}; @@ -261,7 +263,7 @@ impl-attr-macro = {path = "../impl-attr-macro"} # 添加这行 ``` - 编写main/src/main.rs代码如下: -```Rust +```rust // main/src/main.rs use impl_attr_macro::func_info; @@ -272,6 +274,5 @@ fn foo() { fn main() { foo(); // 会自动加上属性宏中的内容 -} +} ``` - diff --git a/src/chapter_3/chapter_3_3.md b/src/chapter_3/chapter_3_3.md index fb42c7d..e7df1e5 100644 --- a/src/chapter_3/chapter_3_3.md +++ b/src/chapter_3/chapter_3_3.md @@ -4,7 +4,7 @@ fn关键字、函数名、函数参数名及其类型(如果有的话)、返回值类型(如果有的话)组成函数签名, 加上由一对花括号包含的函数体组成函数。例子如下: -```Rust +```rust // 一个没有参数,也没有返回值的函数 fn print_line() { println!("++++++++++++++++"); @@ -32,7 +32,7 @@ fn main() { Rust中,函数也可以定义在函数内部,如下: -```Rust +```rust fn calculate(a: u32, b: u32) { println!("a is {:?}", a); println!("b is {:?}", b); @@ -57,7 +57,7 @@ fn main() { Rust中,语句是执行一个写操作但不返回值的指令,表达式则计算并产生一个值。 -```Rust +```rust fn main() { let a = 1u32; // "1u32"就是一个表达式, “let a = 1u32;”则是一个语句 let b = a + 1;// “a + 1”就是一个表达式,“let b = a + 1;”则是一个语句 @@ -74,7 +74,7 @@ fn main() { - 使用return指定返回值,如下: - ```Rust + ```rust fn sum(a: u32, b: u32) -> u32 { let r = a + b; return r //可以加分号,也可以不加分号, 所以这行等价于“return r;” @@ -89,7 +89,7 @@ fn main() { ``` 特别的return关键字不指定值时,表示返回的是(),如下: - ```Rust + ```rust fn my_function() -> () { println!("some thing"); return; //等价于 “return ()” @@ -98,7 +98,7 @@ fn main() { - 不使用return关键字,将返回最后一条执行的表达式的计算结果,如下: - ```Rust + ```rust fn sum(a: u32, b: u32) -> u32 { println!("a is {:?}", a); println!("b is {:?}", b); diff --git a/src/chapter_3/chapter_3_4.md b/src/chapter_3/chapter_3_4.md index 2f6c49f..93f25ee 100644 --- a/src/chapter_3/chapter_3_4.md +++ b/src/chapter_3/chapter_3_4.md @@ -18,7 +18,7 @@ 示例如下: -```Rust +```rust /* * 块注释: * 函数名:sum @@ -48,7 +48,7 @@ Rust提供了cargo doc命令可以把文档注释转换成html网页,最终展 示例如下: -```Rust +```rust // 下面是文档行注释 /// `add_one` 将指定值加1 diff --git a/src/chapter_3/chapter_3_5.md b/src/chapter_3/chapter_3_5.md index 29324c0..8f04c1a 100644 --- a/src/chapter_3/chapter_3_5.md +++ b/src/chapter_3/chapter_3_5.md @@ -11,7 +11,7 @@ Rust中的控制流结构主要包括: - `if`执行条件判断,示例如下: - ```Rust + ```rust fn main() { let a = 2u32; @@ -25,7 +25,7 @@ Rust中的控制流结构主要包括: - `if - else if`处理多重条件,示例如下: - ```Rust + ```rust fn main() { let a = 3u32; @@ -49,7 +49,7 @@ Rust中的控制流结构主要包括: `if`是一个表达式,所以可以在`let`右侧使用,如下: - ```Rust + ```rust fn main() { let a = 3u32; @@ -71,7 +71,7 @@ Rust中的控制流结构主要包括: - loop重复执行代码 - ```Rust + ```rust fn main() { // 一直循环打印 again loop { @@ -82,7 +82,7 @@ Rust中的控制流结构主要包括: - 使用`break`终止循环 - ```Rust + ```rust fn main() { let mut counter = 0; @@ -97,7 +97,7 @@ Rust中的控制流结构主要包括: } ``` 上面的代码将打印10次,遇到`break`后终止循环。另外,`break`也可以返回值,如下: - ```Rust + ```rust fn main() { let mut counter = 0; @@ -115,7 +115,7 @@ Rust中的控制流结构主要包括: - 使用`continue`可以直接跳到下一轮循环 - ```Rust + ```rust fn main() { let mut x = 0; // 此循环将只打印10以内的奇数 @@ -136,7 +136,7 @@ Rust中的控制流结构主要包括: - `while`条件循环执行代码,当条件不满足后结束循环,如下: - ```Rust + ```rust fn main() { let mut cnt = 0u32; while cnt < 10 { @@ -148,7 +148,7 @@ Rust中的控制流结构主要包括: - 在`while`循环中也可以使用`break`和`continue`,如下: - ```Rust + ```rust fn main() { let mut x = 0; @@ -169,7 +169,7 @@ Rust中的控制流结构主要包括: `for`循环用来对一个集合的每个元素执行一些代码,使用方式如下: -```Rust +```rust fn main() { let a = [10, 20, 30, 40, 50]; diff --git a/src/chapter_3/chapter_3_6.md b/src/chapter_3/chapter_3_6.md index c44b2e2..761617d 100644 --- a/src/chapter_3/chapter_3_6.md +++ b/src/chapter_3/chapter_3_6.md @@ -20,7 +20,7 @@ Rust程序使用的内存空间分为如下: “栈和栈帧属于操作系统的概念,由操作系统进行管理,栈空间以后进先出的顺序存储数据。将数据放到栈上就做入栈,将数据移出栈就做出栈。 每次调用函数时,操作系统会在栈顶创建一个栈帧来保存函数的上下文数据(主要是函数内部声明的局部变量),函数返回时返回值也会存储在该栈帧中。当函数调用者取得该函数返回值后,栈帧会被释放。”引用自《Rust入门秘籍》。 -```Rust +```rust fn f1(a: i32, b: i32) -> i32 { let c: i32 = 1; let r = a + b + c; diff --git a/src/chapter_3/chapter_3_7_1.md b/src/chapter_3/chapter_3_7_1.md index fc3d79f..3e4e7db 100644 --- a/src/chapter_3/chapter_3_7_1.md +++ b/src/chapter_3/chapter_3_7_1.md @@ -10,7 +10,7 @@ Rust所有权的规则如下: - 值在任何时刻有且仅有一个所有者; - 当所有者离开作用域后,这个值将丢弃。 -```Rust +```rust fn main() { let a: u32 = 8; let b: String = String::from("hello"); @@ -28,7 +28,7 @@ fn main() { 示例1: -```Rust +```rust fn f() { let b = 1u32; // --------------------------------| let c = 2u32; //-----------| | @@ -50,7 +50,7 @@ fn main() { 示例2: -```Rust +```rust fn main() { let a = 8u32; // --------------------------| { // | @@ -72,7 +72,7 @@ fn main() { - `to_string` - `String::new` -```Rust +```rust fn main() { let s1 = String::from("Hello"); // 方法一 let s2 = "Hello".to_string(); // 方法二 @@ -95,7 +95,7 @@ fn main() { Rust标准库中,String类型的定义如下: -```Rust +```rust pub struct String { vec: Vec, } @@ -146,7 +146,7 @@ struct String { 在Rust中,编译时大小确定的数据放在栈上,编译时大小不能确定的数据放在堆上。考虑如下代码: -```Rust +```rust fn main() { let mut s = String::new(); s.push('A'); @@ -169,7 +169,8 @@ Rust所有权规则第二条,在任意时刻,值有且仅有一个所有者 ### 4.1 完全存储在栈上的类型 考虑如下代码: -```Rust + +```rust fn main() { let x = 5u32; let y = x; @@ -183,7 +184,7 @@ x和y都是u32类型,在编译时知道大小,都存储在栈上。代码第 再考虑如下代码: -```Rust +```rust fn main() { let s = "Hello world!".to_string(); let s1 = s; @@ -222,7 +223,8 @@ s是`String`类型,字符串`"Hello world"`是存储在堆内存上的,其 ## 6. Clone 当需要拷贝堆上的数据时,可以使用`clone`方法,完成深拷贝的操作,如下: -```Rust + +```rust fn main() { let s = "Hello world!".to_string(); let s1 = s.clone(); // 这将发生深拷贝 @@ -250,7 +252,7 @@ Rust中,默认实现了`Copy trait`的类型有: ### 8.1 将值传给函数 在将值传递给函数时,和变量赋值一样会发生值的移动(或复制),如下: -```Rust +```rust fn main() { let s = String::from("hello"); takes_ownership(s); @@ -274,7 +276,7 @@ fn makes_copy(some_integer: i32) { 函数的返回值也可以转移所有权,如下: -```Rust +```rust fn main() { let s1 = gives_ownership(); // gives_ownership 将返回值转移给 s1 let s2 = String::from("hello"); // s2 进入作用域 diff --git a/src/chapter_3/chapter_3_7_2.md b/src/chapter_3/chapter_3_7_2.md index a62f288..2b92120 100644 --- a/src/chapter_3/chapter_3_7_2.md +++ b/src/chapter_3/chapter_3_7_2.md @@ -2,7 +2,7 @@ 考虑如下代码: -```Rust +```rust fn main() { let s2 = String::from("hello"); print(s2); @@ -19,7 +19,7 @@ fn print(s: String) { 如果要在调用`print`函数后仍然能使用s2,根据本书目前学过的Rust知识,则需要将所有权再从函数转移到变量,然后使用,代码如下: -```Rust +```rust fn main() { let s2 = String::from("hello"); let s3 = print(s2); @@ -38,7 +38,7 @@ fn print(s: String) -> String { 引用本质上是一个指针,它存储一个地址,通过它可以访问存储在该地址上属于其它变量的数据。与指针不同的是,引用确保指向某个特性类型的有效值。对于一个变量的引用就是在此变量前面加上&符合。 -```Rust +```rust let a = 5u32; let b = &a; // b是对a的引用 @@ -51,7 +51,7 @@ fn print(s: String) -> String { ![注释](.././assets/10.png) **获取变量的引用,称之为借用** 。通过借用,允许使用被引用变量绑定的值,同时又没有移动该变量的所有权。前面的示例代码可以变成如下: -```Rust +```rust fn main() { let s2 = String::from("hello"); let s3 = &s2; //s3是对s2的借用,s3并不拥有String::from("hello")的所有权,s2的所有权没有改变 @@ -66,7 +66,7 @@ fn print(s: &String) { 在一个范围对变量进行多个引用是可以的,如下: -```Rust +```rust fn main() { let s2 = String::from("hello"); let s3 = &s2; //s3是对s2的借用,s3并不拥有String::from("hello")的所有权,s2的所有权没有改变 @@ -85,7 +85,7 @@ fn print(s: &String) { 引用只能使用变量,并不允许改变变量的值,如果需要改变变量,需要使用可变引用(下节内容),下面的代码会报错: -```Rust +```rust fn main() { let s = String::from("hello"); change(&s); @@ -103,7 +103,7 @@ fn change(some_string: &String) { ### 2.1 使用可变引用 可以通过可变引用改变变量的值,对一个变量加上`&mut`就是对其的可变引用,示例如下: -```Rust +```rust fn main() { let mut s = String::from("hello"); change(&mut s); @@ -153,7 +153,7 @@ fn change(some_string: &mut String) { (1)限制一:**同一作用域,特定数据只能有一个可变引用**。如下代码会报错: -```Rust +```rust fn main() { let mut s1 = String::from("hello"); let r1 = &mut s1; // 可变引用 @@ -164,7 +164,7 @@ fn main() { 但是下面的代码可以的(新老编译器都可以): -```Rust +```rust fn main() { let mut s = String::from("hello"); @@ -182,7 +182,7 @@ fn main() { 下面的代码在新编译器中也是可以的: -```Rust +```rust fn main() { let mut s = String::from("hello"); @@ -198,7 +198,7 @@ fn main() { (2)限制二:**同一作用域,可变引用和不可变引用不能同时存在**。如下代码编译错误: -```Rust +```rust fn main() { let mut s = String::from("hello"); let r1 = &s; // 没问题 @@ -211,7 +211,7 @@ fn main() { 下面的代码在新编译器中是可以的: -```Rust +```rust fn main() { let mut s = String::from("hello"); @@ -277,7 +277,7 @@ int main() 如下代码因为会产生悬垂引用,编译将不会通过: -```Rust +```rust fn main() { let reference_to_nothing = dangle(); } diff --git a/src/chapter_3/chapter_3_7_3.md b/src/chapter_3/chapter_3_7_3.md index 9da82fa..83f9475 100644 --- a/src/chapter_3/chapter_3_7_3.md +++ b/src/chapter_3/chapter_3_7_3.md @@ -13,7 +13,7 @@ Slice(切片)类型,表示引用集合中一段连续的元素序列。Sli Rust中几乎总是使用切片数据的引用。切片数据的引用对应的数据类型描述为`&[T]`或`&mut [T]`,前者不可通过Slice引用来修改源数据,后者可修改源数据。示例如下: -```Rust +```rust fn main(){ let mut arr = [11,22,33,44]; @@ -36,7 +36,7 @@ Slice类型是一个胖指针,包含两个字段: `String`的切片类型为`&str`而不是`&String`,其使用方式如下: -```Rust +```rust fn main() { let s = String::from("hello world!"); let s1 = &s[6..]; // 切片类型&str @@ -54,7 +54,7 @@ fn main() { 数组的Slice,如下: -```Rust +```rust fn main() { let a: [u32; 5] = [1, 2, 3, 4, 5]; let b = &a[1..3]; @@ -64,7 +64,7 @@ fn main() { Vec的Slice,如下: -```Rust +```rust fn main() { let v: Vec = vec![1, 2, 3, 4, 5]; let b = &v[1..3];