From d0c0fe25d98d7c1b57de20475a470059e64c79dc Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Wed, 18 Sep 2024 16:22:36 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=E6=BC=94=E7=A4=BA=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=AD=97=E7=AC=A6=E5=92=8C=E8=BD=AC=E4=B8=BA=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E7=9A=84=E5=86=85=E5=AD=98=E5=8D=A0=E7=94=A8?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E4=B8=8D=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 02_BaseType/Cargo.toml | 1 + 02_BaseType/src/main.rs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/02_BaseType/Cargo.toml b/02_BaseType/Cargo.toml index afd14bc..042f9b0 100644 --- a/02_BaseType/Cargo.toml +++ b/02_BaseType/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + diff --git a/02_BaseType/src/main.rs b/02_BaseType/src/main.rs index 5abc9d0..98e4d3a 100644 --- a/02_BaseType/src/main.rs +++ b/02_BaseType/src/main.rs @@ -23,9 +23,11 @@ fn main() { let c_char = 'z'; let z_char = 'ℤ'; let heart_eyed_cat_char = '😻'; + let y_char = '棒'; let z_string = String::from("棒"); - println!("c_char is {}, z_char is {}, heart_eyed_cat_char is {}", c_char, z_char, heart_eyed_cat_char); - println!("字符'c_char'占用了{}字节的内存大小", std::mem::size_of_val(&c_char)); + println!("c_char is {}, z_char is {}, heart_eyed_cat_char is {}, y_char is {}", c_char, z_char, heart_eyed_cat_char, y_char); + println!("字符'c_char'占用了{}字节的内存大小", size_of_val(&c_char)); + println!("字符'y_char'占用了{}字节的内存大小", size_of_val(&y_char)); println!("字符串'z_string'内容占用了{}字节的内存大小", &z_string.as_bytes().len()); let tup_var: (i32, f64, u8, char) = (-500, 6.4, 1, 'z'); @@ -36,4 +38,4 @@ fn main() { let first_element = a_array[0]; let second_element = a_array[1]; println!("The first element is {}, the second element is {}", first_element, second_element); -} \ No newline at end of file +} From d3b241b30b309ca526b881adfd77d5f401b8b8dd Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Wed, 18 Sep 2024 16:30:55 +0800 Subject: [PATCH 02/18] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E4=BE=9D=E8=B5=96=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 03_CompoundType/Cargo.toml | 2 +- 03_CompoundType/src/main.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/03_CompoundType/Cargo.toml b/03_CompoundType/Cargo.toml index 32be50a..5801edc 100644 --- a/03_CompoundType/Cargo.toml +++ b/03_CompoundType/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -itertools = "0.10" + diff --git a/03_CompoundType/src/main.rs b/03_CompoundType/src/main.rs index 643f36f..6d20ab8 100644 --- a/03_CompoundType/src/main.rs +++ b/03_CompoundType/src/main.rs @@ -1,5 +1,3 @@ -use itertools::Itertools; - fn main() { let mixed = ("Rust", 2023, 3.14, true); let (lang, year, pi, status) = mixed; @@ -15,7 +13,7 @@ fn main() { println!("vector v1: {:?}", &v1); // 2.使用宏 vec! 来创建数组,支持在创建时就给予初始化值 - let v2 = vec![1u8, 2, 3]; + let v2 = vec![1u8, 2, 3]; println!("vector v2: {:?}", &v2); let mut s = String::from("Hello"); // 可变的String类型 @@ -27,7 +25,6 @@ fn main() { let numbers = [1, 2, 3, 4, 5]; - // 给数组 numbers 增加数字 6和7 let slice = &numbers[0..2]; // 引用数组的一部分 From 873de154a8ac7975da82e539b6645f01c12b3d0c Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Wed, 18 Sep 2024 17:33:12 +0800 Subject: [PATCH 03/18] format code --- 04_Struct_Enum/src/main.rs | 3 +-- 05_Ownership/src/main.rs | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/04_Struct_Enum/src/main.rs b/04_Struct_Enum/src/main.rs index a89a931..0ce3d80 100644 --- a/04_Struct_Enum/src/main.rs +++ b/04_Struct_Enum/src/main.rs @@ -1,4 +1,3 @@ - // 定义一个Person结构体 struct Person { name: String, @@ -28,4 +27,4 @@ fn main() { // 调用这个函数,不需要 owner show_info(); -} \ No newline at end of file +} diff --git a/05_Ownership/src/main.rs b/05_Ownership/src/main.rs index 9c5b6e8..df2dee9 100644 --- a/05_Ownership/src/main.rs +++ b/05_Ownership/src/main.rs @@ -7,10 +7,11 @@ fn calculate_length(s: &String) -> usize { s.len() } -fn change(some_string: &mut String) -> &mut String{ +fn change(some_string: &mut String) -> &mut String { some_string.push_str(", wtf!"); some_string } + fn main() { let mut s1 = String::from("hello"); // s1 是 hello 对象的所有者 s1.push_str(", wtf!"); // s1 可以修改 hello 对象,追加字符串 @@ -30,4 +31,4 @@ fn main() { let mut s4 = String::from("hello"); change(&mut s4); // s4 发生可变借用,函数可以修改 s4 println!("{}", s4); -} \ No newline at end of file +} From 4b7862faa4c2f77bbef97a7882970da7df0b09ea Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Thu, 19 Sep 2024 15:10:13 +0800 Subject: [PATCH 04/18] format code --- 06_Function/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/06_Function/src/main.rs b/06_Function/src/main.rs index d155a30..2b63fa5 100644 --- a/06_Function/src/main.rs +++ b/06_Function/src/main.rs @@ -7,7 +7,7 @@ fn print_sum(a: i32, b: i32) { } fn add_two(a: i32) -> i32 { - return a + 2 + a + 2 // 或 `return a + 2;` } @@ -63,4 +63,4 @@ fn main() { break; } } -} \ No newline at end of file +} From 25ae98be8a71cb114ad885e53016cada4fef0559 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 22 Sep 2024 16:00:59 +0800 Subject: [PATCH 05/18] format code --- 19_IO/Cargo.toml | 2 +- 19_IO/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/19_IO/Cargo.toml b/19_IO/Cargo.toml index 5602a43..5103fda 100644 --- a/19_IO/Cargo.toml +++ b/19_IO/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1", features = ["full"] } \ No newline at end of file +tokio = { version = "1.40.0", features = ["full"] } diff --git a/19_IO/src/main.rs b/19_IO/src/main.rs index a4fd3ec..a280f2f 100644 --- a/19_IO/src/main.rs +++ b/19_IO/src/main.rs @@ -8,4 +8,4 @@ async fn main() { file.read_to_end(&mut contents).await.expect("Failed to read data"); println!("File contents: {:?}", String::from_utf8(contents).expect("Data not UTF-8")); -} \ No newline at end of file +} From 6a97659723289c5457c8d4fbd062233e05ea6de8 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 22 Sep 2024 16:52:07 +0800 Subject: [PATCH 06/18] format code --- 18_Async/Cargo.toml | 2 +- 18_Async/README.md | 22 +++++++++++++++------- 18_Async/src/main.rs | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/18_Async/Cargo.toml b/18_Async/Cargo.toml index 84488d6..b58b608 100644 --- a/18_Async/Cargo.toml +++ b/18_Async/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1", features = ["full"] } \ No newline at end of file +tokio = { version = "1.40.0", features = ["full"] } diff --git a/18_Async/README.md b/18_Async/README.md index b330bcb..be97405 100644 --- a/18_Async/README.md +++ b/18_Async/README.md @@ -12,7 +12,8 @@ tags: ## 异步编程基础 -异步编程是处理并发编程的一种高效方式,特别适合 I/O 密集型或需要高响应速度的应用。在 Rust 中,异步编程主要依赖于 `async` 关键字标记的异步函数和 `await` 表达式。 +异步编程是处理并发编程的一种高效方式,特别适合 I/O 密集型或需要高响应速度的应用。在 Rust 中,异步编程主要依赖于 `async` +关键字标记的异步函数和 `await` 表达式。 ### 异步函数与 `async/await` @@ -41,9 +42,13 @@ async fn main() { ### `Future` 概念 -`Future` trait 定义了一个异步操作,这个操作可能还没有完成。`Future` 可以通过调用 `poll` 函数来推进,该函数决定是否继续等待或者输出最终结果。如果 `Future` 完成了,将返回 `Poll::Ready(result)`。如果 `Future` 还不能完成,将返回 `Poll::Pending` 并安排在 `Future` 准备好做出更多进展时调用 `wake()` 函数。当 `wake()` 被调用时,驱动 `Future` 的执行器将再次调用 `poll`,以便 `Future` 可以取得更多进展。 +`Future` trait 定义了一个异步操作,这个操作可能还没有完成。`Future` 可以通过调用 `poll` 函数来推进,该函数决定是否继续等待或者输出最终结果。如果 +`Future` 完成了,将返回 `Poll::Ready(result)`。如果 `Future` 还不能完成,将返回 `Poll::Pending` 并安排在 `Future` +准备好做出更多进展时调用 `wake()` 函数。当 `wake()` 被调用时,驱动 `Future` 的执行器将再次调用 `poll`,以便 `Future` +可以取得更多进展。 #### 示例:简化版本的Future + ```rust trait SimpleFuture { type Output; @@ -64,15 +69,16 @@ trait Future { fn poll( // 注意从 &mut self 到 Pin<&mut Self> 的变化: self: Pin<&mut Self>, - // 以及从 wake: fn() 到 cx: &mut Context<'_> 的变化: - cx: &mut Context<'_>, + // 以及从 wake: fn() 到 _cx: &mut Context<'_> 的变化: + _cx: &mut Context<'_>, ) -> Poll; } ``` ### 异步任务的运行 -异步任务是由 `Future` 变体驱动的,通常是通过一个执行器来调度和执行。执行器负责管理 `Future` 的状态并在合适的时候调用 `poll` 方法。 +异步任务是由 `Future` 变体驱动的,通常是通过一个执行器来调度和执行。执行器负责管理 `Future` 的状态并在合适的时候调用 +`poll` 方法。 #### 示例:手动实现自定义Future @@ -98,6 +104,8 @@ async fn main() { } ``` -在这个例子中,`SimpleFuture` 是一个自定义的 `Future`,它立即返回一个结果,没有延时或等待。通过使用 `await`,我们可以在异步函数中等待这个 `Future` 完成。 +在这个例子中,`SimpleFuture` 是一个自定义的 `Future`,它立即返回一个结果,没有延时或等待。通过使用 `await`,我们可以在异步函数中等待这个 +`Future` 完成。 -通过对这些基础和核心概念的理解,你可以开始构建更复杂的异步应用程序,这些程序能够有效地执行多个操作,而不会互相阻塞。在未来的节中,我们将探讨如何结合多个 `Future` 并发处理复杂的异步逻辑。 \ No newline at end of file +通过对这些基础和核心概念的理解,你可以开始构建更复杂的异步应用程序,这些程序能够有效地执行多个操作,而不会互相阻塞。在未来的节中,我们将探讨如何结合多个 +`Future` 并发处理复杂的异步逻辑。 \ No newline at end of file diff --git a/18_Async/src/main.rs b/18_Async/src/main.rs index 16e13d4..459dc68 100644 --- a/18_Async/src/main.rs +++ b/18_Async/src/main.rs @@ -3,10 +3,10 @@ use std::pin::Pin; use std::task::{Context, Poll}; struct SimpleFuture; -impl Future for SimpleFuture{ +impl Future for SimpleFuture { type Output = String; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { Poll::Ready("completed".to_string()) } } From 4f6a4a45db3a93aefe300daf6d56ac5de81cedb6 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 22 Sep 2024 17:14:46 +0800 Subject: [PATCH 07/18] format code --- 17_Lock/Cargo.toml | 1 + 17_Lock/README.md | 17 ++++++++++------- 17_Lock/src/main.rs | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/17_Lock/Cargo.toml b/17_Lock/Cargo.toml index aa42bae..a7d7448 100644 --- a/17_Lock/Cargo.toml +++ b/17_Lock/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + diff --git a/17_Lock/README.md b/17_Lock/README.md index b38c226..2203389 100644 --- a/17_Lock/README.md +++ b/17_Lock/README.md @@ -1,15 +1,16 @@ --- title: Locks tags: -- Rust -- Concurrency -- Locks -- wtfacademy + - Rust + - Concurrency + - Locks + - wtfacademy --- # WTF Rust 极简入门: 使用锁管理并发 -在并发编程中,锁是用于管理多个线程间对共享资源访问的同步机制。在 Rust 中,互斥锁(Mutex)和读写锁(RwLock)是最常用来控制数据对线程安全访问的两种锁。本章将详细介绍这两种锁的使用方法和最佳实践。 +在并发编程中,锁是用于管理多个线程间对共享资源访问的同步机制。在 Rust +中,互斥锁(Mutex)和读写锁(RwLock)是最常用来控制线程对数据安全访问的两种锁。本章将详细介绍这两种锁的使用方法和最佳实践。 ## 互斥锁(Mutex) @@ -17,7 +18,8 @@ tags: ### 基本使用 -当你使用 `Mutex` 时,需要调用 `lock` 方法来访问数据独占权。这个方法会阻塞其他线程访问贡献数据直到获取到锁。由于 `Drop` 的调用,一旦线程完成数据操作,锁会在作用域结束时自动释放。 +当你使用 `Mutex` 时,需要调用 `lock` 方法来访问数据独占权。这个方法会阻塞其他线程访问共享数据直到获取到锁。由于 `Drop` +的调用,一旦线程完成数据操作,锁会在作用域结束时自动释放。 #### 示例 @@ -94,7 +96,8 @@ fn main() { ### 注意事项 - **写锁饥饿(Writer Starvation)**:发生在写者尝试获取锁时因为持续的读操作而被不断延迟的情况。在许多RwLock实现中,为了保证读操作的高效性,允许多个读者同时持有锁,这可能导致写者长时间等待。 -- **读锁饥饿(Reader Starvation)**:当 `RwLock` 实现倾向于写锁时,可能会出现读锁饥饿的情况。这意味着一旦有写者在等待,即使锁当前可用,新的读请求也会被阻塞,直到写锁被释放。这种策略可以防止写锁饥饿,但如果写请求频繁,读者可能会遭受长时间的等待。 +- **读锁饥饿(Reader Starvation)**:当 `RwLock` + 实现倾向于写锁时,可能会出现读锁饥饿的情况。这意味着一旦有写者在等待,即使锁当前可用,新的读请求也会被阻塞,直到写锁被释放。这种策略可以防止写锁饥饿,但如果写请求频繁,读者可能会遭受长时间的等待。 - **锁升级和降级**:Rust 的标准库 `RwLock` 不支持锁升级(持有读锁的情况下请求写锁)或锁降级(持有写锁的情况下转换为读锁)。 通过使用这些锁,你可以在 Rust 中安全地管理并发访问共享数据。正确地使用锁可以帮助你避免多线程编程中常见的问题,如数据竞争、死锁和并发性能问题。 \ No newline at end of file diff --git a/17_Lock/src/main.rs b/17_Lock/src/main.rs index 8791f41..e7d830b 100644 --- a/17_Lock/src/main.rs +++ b/17_Lock/src/main.rs @@ -91,4 +91,4 @@ fn demo_deadlock() { // handle.join().unwrap(); // println!("Value after supposed lock upgrade: {}", *lock.read().unwrap()); -//} \ No newline at end of file +//} From 96c3235a039b2a7847d6db8960a62825f1f51347 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 22 Sep 2024 17:19:24 +0800 Subject: [PATCH 08/18] format code --- 16_Channel/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/16_Channel/src/main.rs b/16_Channel/src/main.rs index ee9970d..2606bb9 100644 --- a/16_Channel/src/main.rs +++ b/16_Channel/src/main.rs @@ -12,4 +12,4 @@ fn main() { let received = rx.recv().unwrap(); // `recv`会阻塞主线程直到从通道接收到消息 println!("Received: {}", received); -} \ No newline at end of file +} From 2b81309e242d4fff55bd46fc05e553ae14580c17 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Fri, 27 Sep 2024 14:23:11 +0800 Subject: [PATCH 09/18] format code --- 15_Thread/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/15_Thread/src/main.rs b/15_Thread/src/main.rs index a7e9579..43efe0c 100644 --- a/15_Thread/src/main.rs +++ b/15_Thread/src/main.rs @@ -21,4 +21,4 @@ fn main() { } println!("All threads have finished."); -} \ No newline at end of file +} From c3ee9a7809f2a082aa8ca6adf7ecbe430e536864 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Fri, 27 Sep 2024 14:59:28 +0800 Subject: [PATCH 10/18] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BF=87=E6=97=B6?= =?UTF-8?q?=E7=9A=84=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 14_Rust_libs/Cargo.toml | 8 ++++---- 14_Rust_libs/src/main.rs | 35 ++++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/14_Rust_libs/Cargo.toml b/14_Rust_libs/Cargo.toml index 1f3d034..bfaacff 100644 --- a/14_Rust_libs/Cargo.toml +++ b/14_Rust_libs/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1", features = ["full"] } -reqwest = { version = "0.11", features = ["json"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" \ No newline at end of file +tokio = { version = "1.40.0", features = ["full"] } +reqwest = { version = "0.12.7", features = ["json"] } +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" diff --git a/14_Rust_libs/src/main.rs b/14_Rust_libs/src/main.rs index 114cd5c..03456bf 100644 --- a/14_Rust_libs/src/main.rs +++ b/14_Rust_libs/src/main.rs @@ -1,5 +1,5 @@ // 引入所需的库 -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use serde_json::Value; use std::error::Error as StdError; @@ -36,7 +36,7 @@ async fn main() -> Result<(), Box> { println!("deserialized = {:?}", deserialized); // 发起网络请求并获取响应 - let resp = reqwest::get("https://mock.apifox.com/m1/4333142-0-default/user") + let resp = reqwest::get("https://apifoxmock.com/m1/4333142-0-default/user") .await? .text() .await?; @@ -52,20 +52,33 @@ async fn main() -> Result<(), Box> { // 假设对方 json 格式未知,则: - let url = "https://mock.apifox.com/m1/4333142-0-default/orders"; // 替换为实际的接口URL + let url = "https://apifoxmock.com/m1/4333142-0-default/orders"; // 替换为实际的接口URL match fetch_and_parse_json(url).await { - Ok(json) => { - // 这里可以动态地访问和解析JSON数据 - println!("{:?}", json); + Ok(data) => { + // 这里可以动态地访问和解析JSON 数据 + println!("访问 URL 获取的数据: {:?}", data); - // 例如,检查某个键是否存在并获取对应的值 - if let Some(data) = json.get("some_key") { - println!("Data under 'some_key': {:?}", data); + // 假设解析的 JSON 数据是数组 + if let Some(parsed) = data.as_array() { + // 遍历数组并获取其中的字段 + for item in parsed { + // 例如,检查某个键是否存在并获取对应的值 + if let (Some(id), Some(order_number), Some(price)) = ( + item.get("id").and_then(|v| v.as_i64()), + item.get("order_number").and_then(|v| v.as_str()), + item.get("price").and_then(|v| v.as_str()), + ) { + println!("id: {}, order_number: {}, price: {}", id, order_number, price); + } + } + } else { + println!("不是一个 JSON 数组"); } - }, + } Err(e) => { eprintln!("Error: {}", e); } } Ok(()) -} \ No newline at end of file +} + From 76048013a3bcdfdeda0a48e7fbebec4f28df96cc Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Fri, 27 Sep 2024 15:38:38 +0800 Subject: [PATCH 11/18] format code --- 13_Cargo/Cargo.toml | 4 ++-- 13_Cargo/README.md | 23 ++++++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/13_Cargo/Cargo.toml b/13_Cargo/Cargo.toml index 6d39f30..c9c25d7 100644 --- a/13_Cargo/Cargo.toml +++ b/13_Cargo/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = "1.0" +serde = "1.0.210" [workspace] members = [ "crate1", "crate2", -] \ No newline at end of file +] diff --git a/13_Cargo/README.md b/13_Cargo/README.md index 9dcce10..6bd6bc4 100644 --- a/13_Cargo/README.md +++ b/13_Cargo/README.md @@ -1,18 +1,20 @@ --- title: Cargo tags: -- Rust -- basic -- wtfacademy + - Rust + - basic + - wtfacademy --- # WTF Rust 极简入门: Cargo 管理 -在本章中,我们将讨论如何使用 Rust 的包管理工具 Cargo 来管理项目依赖。我们将首先探讨 Cargo 的基本功能,然后深入研究如何添加、更新和管理外部库依赖项。最后,我们会介绍一些 Cargo 的高级功能,如如何使用工作空间管理多个包。 +在本章中,我们将讨论如何使用 Rust 的包管理工具 Cargo 来管理项目依赖。我们将首先探讨 Cargo +的基本功能,然后深入研究如何添加、更新和管理外部库依赖项。最后,我们会介绍一些 Cargo 的高级功能,如如何使用工作空间管理多个包。 ## 理解Cargo -Cargo是 Rust 的官方包管理工具(使用 rustup 安装 Rust 时,会默认安装 Cargo),负责多方面的任务:创建和管理 rust 模块系统、下载并编译依赖库、构建软件包等。它还能够确保所有依赖项的版本兼容性,让项目构建更可靠。可以使用以下命令检查Cargo是否安装以及查看Cargo版本: +Cargo是 Rust 的官方包管理工具(使用 rustup 安装 Rust 时,会默认安装 Cargo),负责多方面的任务:创建和管理 rust +模块系统、下载并编译依赖库、构建软件包等。它还能够确保所有依赖项的版本兼容性,让项目构建更可靠。可以使用以下命令检查Cargo是否安装以及查看Cargo版本: ``` cargo --version @@ -20,7 +22,8 @@ cargo --version ### 初始化项目 -要开始使用 Cargo 管理 Rust 项目,首先需要创建一个 Cargo 配置文件(`Cargo.toml`)。这个文件描述了项目的基本信息和依赖。使用命令`cargo new `能够快速生成一个新的项目目录,里面包含了基本的项目结构和`Cargo.toml`文件。 +要开始使用 Cargo 管理 Rust 项目,首先需要创建一个 Cargo 配置文件(`Cargo.toml`)。这个文件描述了项目的基本信息和依赖。使用命令 +`cargo new `能够快速生成一个新的项目目录,里面包含了基本的项目结构和`Cargo.toml`文件。 ``` cargo new project_name @@ -39,7 +42,8 @@ cargo run ### 1. 手动添加 -在`Cargo.toml`文件中,你可以在`[dependencies]`节下添加所需的库依赖。Cargo支持从[crates.io](https://crates.io/)(Rust的官方包仓库)、GitHub或本地路径等多种来源获取依赖。 +在`Cargo.toml`文件中,你可以在`[dependencies]`节下添加所需的库依赖。Cargo支持从[crates.io](https://crates.io/) +(Rust的官方包仓库)、GitHub或本地路径等多种来源获取依赖。 假设你想要添加一个名为"serde"的序列化库,你可以在`Cargo.toml`的`[dependencies]`节下添加以下代码: @@ -60,7 +64,8 @@ cargo add serde ## 使用 Cargo 的工作空间 -对于包含多个包的大型项目,使用 Cargo 的工作空间功能能够高效管理依赖与构建。工作空间由一个顶层的`Cargo.toml`配置文件定义,它链接到多个子项目。 +对于包含多个包的大型项目,使用 Cargo 的工作空间功能能够高效管理依赖与构建。工作空间由一个顶层的`Cargo.toml` +配置文件定义,它链接到多个子项目。 ### 创建工作空间 @@ -74,7 +79,7 @@ members = [ ] ``` -每个成员目录都有自己的`Cargo.toml`文件和源代码。这样设置后,Car工作空间中的所有包能共享依赖,提高编译效率。 +每个成员目录都有自己的`Cargo.toml`文件和源代码。这样设置后,Cargo工作空间中的所有包能共享依赖,提高编译效率。 ## 高级功能 From d254bb20dd763235c2297d0689defda2a50d13d1 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 29 Sep 2024 18:54:36 +0800 Subject: [PATCH 12/18] format code --- 09_Errors/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/09_Errors/src/main.rs b/09_Errors/src/main.rs index e7b028b..22a4931 100644 --- a/09_Errors/src/main.rs +++ b/09_Errors/src/main.rs @@ -55,6 +55,5 @@ fn main() { } panic!("This is a critical error!"); - } From a2416c31a7a77fbd1ebef9d7695c24a4898e5047 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 29 Sep 2024 19:38:11 +0800 Subject: [PATCH 13/18] format code --- 10_Trait/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/10_Trait/src/main.rs b/10_Trait/src/main.rs index a7e2730..51f978c 100644 --- a/10_Trait/src/main.rs +++ b/10_Trait/src/main.rs @@ -3,7 +3,7 @@ trait Describable { fn describe(&self) -> String; // 默认实现的方法,可以被实现该 trait 的类型覆盖 - fn default_descriptiton(&self) -> String { + fn default_description(&self) -> String { String::from("This is default functions") } } @@ -62,7 +62,7 @@ fn main() { println!("{}", alice.describe()); // 调用 default_description 方法 - println!("{}", alice.default_descriptiton()); + println!("{}", alice.default_description()); // 使用 output_description 函数 output_description(&alice); From f2001e38f7b9720d3b203f3b12c8aaba84c7af35 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Sun, 29 Sep 2024 19:57:16 +0800 Subject: [PATCH 14/18] format code --- 11_Generics/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/11_Generics/src/main.rs b/11_Generics/src/main.rs index b157137..a170425 100644 --- a/11_Generics/src/main.rs +++ b/11_Generics/src/main.rs @@ -112,4 +112,4 @@ fn main() { // 创建一个整数向量,并使用 largest 函数找到其中的最大值 let numbers = vec![34, 50, 25, 100, 65]; println!("The largest number is {}", largest(&numbers)); -} \ No newline at end of file +} From 0771c25810d893a905f150426740936927da6422 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Mon, 30 Sep 2024 17:24:06 +0800 Subject: [PATCH 15/18] format code --- .../rust_tests.md => 07_Tests/README.md | 356 ++++++++++-------- {07_Rust_Tests => 07_Tests}/adder/Cargo.toml | 2 +- {07_Rust_Tests => 07_Tests}/adder/src/lib.rs | 85 ++--- .../adder/tests/common/mod.rs | 2 +- .../adder/tests/integration_test.rs | 0 5 files changed, 243 insertions(+), 202 deletions(-) rename 07_Rust_Tests/rust_tests.md => 07_Tests/README.md (51%) rename {07_Rust_Tests => 07_Tests}/adder/Cargo.toml (85%) rename {07_Rust_Tests => 07_Tests}/adder/src/lib.rs (73%) rename {07_Rust_Tests => 07_Tests}/adder/tests/common/mod.rs (96%) rename {07_Rust_Tests => 07_Tests}/adder/tests/integration_test.rs (100%) diff --git a/07_Rust_Tests/rust_tests.md b/07_Tests/README.md similarity index 51% rename from 07_Rust_Tests/rust_tests.md rename to 07_Tests/README.md index 9f20d23..64d196a 100644 --- a/07_Rust_Tests/rust_tests.md +++ b/07_Tests/README.md @@ -1,9 +1,9 @@ --- title: 7. Test tags: -- Rust -- basic -- test + - Rust + - basic + - test --- # WTF Rust 极简入门: Rust 测试 @@ -20,30 +20,34 @@ tags: 本文主要介绍了 Rust 测试的函数、命令和种类, 干货满满。 -- 测试函数: 测试中常用的宏、属性和枚举, 包括 `#[test]` 、`#[cfg(test)]`、 `panic!` 、`assert!` 、`assert_eq!` 、 `assert_ne!` 、`should_panic` 、`Result` 。 +- 测试函数: 测试中常用的宏、属性和枚举, 包括 `#[test]` 、`#[cfg(test)]`、 `panic!` 、`assert!` 、`assert_eq!`、`assert_ne!` 、 + `should_panic` 、`Result` 。 -- 测试命令 `cargo test`: 并行或连续的运行测试 `--test-threads`; 显示函数的输出 `--show-output`、运行单个测试、多个测试 `cargo test 函数名` `cargo test --test 文件名`; 忽略部分测试 `#[ignore]` 、`--ignored` 。 +- 测试命令 `cargo test`: 并行或连续的运行测试 `--test-threads`; 显示函数的输出 `--show-output`、运行单个测试、多个测试 + `cargo test 函数名` `cargo test --test 文件名`; 忽略部分测试 `#[ignore]` 、`--ignored` 。 - 测试种类: 单元测试(测试私有函数)、集成测试(tests 目录、共享子模块、二进制 binary crate) ## 1. 测试函数 -测试函数是带有  `test`  属性标注的函数。使用  `cargo test`  命令运行测试时, 会调用标记了  `test`  属性的函数。使用 Cargo 新建一个库项目时,它会自动为我们生成一个测试模块和一个测试函数,  `tests`  模块还可以包括非测试的函数(无 test 属性的函数)来进行常见操作, 可以添加任意数量的 test 模块或函数。 +测试函数是带有`test`属性标注的函数。使用`cargo test`命令运行测试时, 会调用标记了`test`属性的函数。使用 Cargo +新建一个库项目时,它会自动为我们生成一个测试模块和一个测试函数,`tests`模块还可以包括非测试的函数(无 test 属性的函数) +来进行常见操作, 可以添加任意数量的 test 模块或函数。
Tips: 安装 Rust 在 Linux 或 macOS 上安装 rustup -```rust -$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh +```shell +$ curl - - proto ' = https' - - tlsv1.2 https: //sh.rustup.rs -sSf | sh ``` -在 Windows 上, 访问  [安装页面](https://www.rust-lang.org/tools/install) 页面并按照说明安装 Rust。 +在 Windows 上, 访问[安装页面](https://www.rust-lang.org/tools/install) 页面并按照说明安装 Rust。 更新和卸载、以及查看是否正确安装了 Rust -```rust +```shell $ rustup update $ rustup self uninstall -$ rustc --version +$ rustc - - version ``` 安装 Rust, 具体参见[安装指南](https://doc.rust-lang.org/book/ch01-01-installation.html); @@ -60,9 +64,9 @@ Rust 的属性(attribute)是关于 Rust 代码片段的元数据, 它不会 ### 1.1 创建一个新的库项目  `adder` -```rust -$ cargo new adder --lib - Created library `adder` project +```shell +$ cargo new adder - - lib +Created library `adder` project $ cd adder ``` @@ -78,24 +82,27 @@ mod tests { } ``` -`cargo test`  命令构建一个 Test Runner 可执行文件, 它会运行项目中所有的测试函数。测试函数的名称 `exploration` 和测试的运行结果 `ok`。`1 passed; 0 failed; 0 ignored; 0 filtered out;`  表示通过、失败、忽略和过滤的测试数量, `0 measured`  统计是针对[性能测试](https://doc.rust-lang.org/unstable-book/library-features/test.html), `Doc-tests adder`  是所有文档注释的测试结果, 确保文档和实际代码同步。 +`cargo test`命令构建一个 Test Runner 可执行文件, 它会运行项目中所有的测试函数。测试函数的名称 `exploration` 和测试的运行结果 +`ok`。`1 passed; 0 failed; 0 ignored; 0 filtered out;`表示通过、失败、忽略和过滤的测试数量, `0 measured` +统计是针对[性能测试](https://doc.rust-lang.org/unstable-book/library-features/test.html), `Doc-tests adder`是所有文档注释的测试结果, +确保文档和实际代码同步。 -```solidity +```shell $ cargo test - Compiling adder v0.1.0 (file:///projects/adder) - Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs - Running target/debug/deps/adder-ce99bcc2479f4607 +Compiling adder v0.1.0 (file : ///projects/adder) +Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs +Running target / debug / deps/ adder - ce99bcc2479f4607 running 1 test -test tests::it_works ... ok +test tests :: it_works ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result : ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Doc-tests adder +Doc - tests adder running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result : ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` @@ -105,7 +112,9 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out #### 1.2.1 `panic!` 宏 -测试失败的例子: 测试函数触发 panic 时, 测试会失败, 触发 panic 最简单的办法就是使用 `panic!`  宏。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败, 列举测试失败的详细原因, 并且列出所有失败的测试。 +测试失败的例子: 测试函数触发 panic 时, 测试会失败, 触发 panic 最简单的办法就是使用 `panic!` +宏。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败, 列举测试失败的详细原因, +并且列出所有失败的测试。
点我展开示例代码 @@ -125,19 +134,19 @@ mod tests { ``` -```rust +```text running 2 tests test tests::exploration ... ok test tests::another ... FAILED failures: ----- tests::another stdout ---- -thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:9 +- - - - tests::another stdout - - - - +thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10: 9 note: Run with `RUST_BACKTRACE=1` for a backtrace. failures: - tests::another +tests::another test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out @@ -150,49 +159,51 @@ error: test failed #### 1.2.2 断言 (Assert) 宏: `assert!` 、`assert_eq!`  和  `assert_ne!` 检查代码是否按照期望返回正确的值 - `assert!` 宏 - 标准库的断言 `assert!`  宏用于检查代码, 参数为布尔值。如果值为 true, 测试通过。如果值为 false, `assert!`  会调用  `panic!`  宏, 测试失败。使用 glob 全局导入外部模块所有内容 `use super::*;`,以便在  `tests`  模块中使用所有在外部模块定义的内容。 + +标准库的断言 `assert!`宏用于检查代码, 参数为布尔值。如果值为 true, 测试通过。如果值为 false, `assert!`会调用`panic!`宏, +测试失败。使用 glob 全局导入外部模块所有内容 `use super::*;`,以便在`tests`模块中使用所有在外部模块定义的内容。
点我展开示例代码 ```rust #[derive(Debug)] - pub struct Rectangle { - width: u32, - height: u32, - } +pub struct Rectangle { + width: u32, + height: u32, +} - impl Rectangle { - pub fn can_hold(&self, other: &Rectangle) -> bool { - self.width < other.width && self.height > other.height - } - } +impl Rectangle { + pub fn can_hold(&self, other: &Rectangle) -> bool { + self.width < other.width && self.height > other.height + } +} ``` ```rust #[cfg(test)] - mod tests { - use super::*; +mod tests { + use super::*; - #[test] - fn larger_can_hold_smaller() { - let larger = Rectangle { width: 8, height: 7 }; - let smaller = Rectangle { width: 5, height: 1 }; + #[test] + fn larger_can_hold_smaller() { + let larger = Rectangle { width: 8, height: 7 }; + let smaller = Rectangle { width: 5, height: 1 }; - assert!(larger.can_hold(&smaller)); - } + assert!(larger.can_hold(&smaller)); + } - #[test] - fn smaller_cannot_hold_larger() { - let larger = Rectangle { width: 8, height: 7 }; - let smaller = Rectangle { width: 5, height: 1 }; + #[test] + fn smaller_cannot_hold_larger() { + let larger = Rectangle { width: 8, height: 7 }; + let smaller = Rectangle { width: 5, height: 1 }; - assert!(!smaller.can_hold(&larger)); - } - } + assert!(!smaller.can_hold(&larger)); + } +} ``` - ```rust + ```shell running 2 tests test tests::smaller_cannot_hold_larger ... ok test tests::larger_can_hold_smaller ... ok @@ -202,32 +213,38 @@ error: test failed
-- `assert_eq!`  和  `assert_ne!` - `assert_eq!`  和  `assert_ne!` 用于检查测试代码的值与期望值是否相等, 相当于 `assert!`宏的参数是 `==`  或 `!=` 运算符的表达式。区别是断言失败时他们会打印出这两个具体的值,而  `assert!`  只会打印出  `false`  值。`assert_eq!`  宏在传递给它的两个值相等时通过,不相等时失败, `assert_ne!`  宏则与之相反。 - 注意: 在一些语言和测试框架中,断言两个值相等的函数的参数叫做  `expected`  和  `actual`,而且指定参数的顺序是很关键的。但是在 Rust 中,他们叫做  `left`  和  `right`,同时指定参数的顺序并不重要。 - `assert_eq!`  和  `assert_ne!`  宏在底层分别使用了  `==`  和  `!=`。当断言失败时,这些宏会使用调试格式打印出其参数,这意味着被比较的值必需实现了  `PartialEq`  和  `Debug` trait。所有的基本类型和大部分标准库类型都实现了这两个派生的 trait。自定义的结构体和枚举并没有实现这些 trait ,所以需要添加  `#[derive(PartialEq, Debug)]`  标注, 参见  [“可派生的 trait”](https://doc.rust-lang.org/book/appendix-03-derivable-traits.html) 。 +- `assert_eq!`和`assert_ne!` + +`assert_eq!`和`assert_ne!` 用于检查测试代码的值与期望值是否相等, 相当于 `assert!`宏的参数是 `==`或 `!=` +运算符的表达式。区别是断言失败时他们会打印出这两个具体的值,而`assert!`只会打印出`false`值。`assert_eq!` +宏在传递给它的两个值相等时通过,不相等时失败, `assert_ne!`宏则与之相反。 +注意: 在一些语言和测试框架中,断言两个值相等的函数的参数叫做`expected`和`actual`,而且指定参数的顺序是很关键的。但是在 +Rust 中,他们叫做`left`和`right`,同时指定参数的顺序并不重要。 +`assert_eq!`和`assert_ne!`宏在底层分别使用了`==`和`!=`。当断言失败时,这些宏会使用调试格式打印出其参数,这意味着被比较的值必需实现了 +`PartialEq`和`Debug`trait。所有的基本类型和大部分标准库类型都实现了这两个派生的 trait。自定义的结构体和枚举并没有实现这些 +trait ,所以需要添加`#[derive(PartialEq, Debug)]`标注, +参见[“可派生的 trait”](https://doc.rust-lang.org/book/appendix-03-derivable-traits.html)。
点我展开示例代码 ```rust pub fn add_two(a: i32) -> i32 { - a + 2 - } - - #[cfg(test)] - mod tests { - use super::\*; + a + 2 +} - #[test] - fn it_adds_two() { - assert_eq!(4, add_two(2)); - } +#[cfg(test)] +mod tests { + use super::*; - } + #[test] + fn it_adds_two() { + assert_eq!(4, add_two(2)); + } +} ``` - ```rust + ```shell running 1 test test tests::it_adds_two ... ok @@ -238,7 +255,8 @@ error: test failed - 自定义失败信息 - 自定义信息可以作为参数传递给`assert!` 、`assert_eq!`  和  `assert_ne!` 。`String`  的大小可以增加,其内容也可以改变, 使用  `+`  运算符或  `format!`  宏来拼接  `String`  值。为测试函数增加一个自定义失败信息参数:带`{}`占位符的格式字符串,以及  `greeting`  函数的值。 + 自定义信息可以作为参数传递给`assert!` 、`assert_eq!`和`assert_ne!` 。`String`的大小可以增加,其内容也可以改变, 使用`+` + 运算符或`format!`宏来拼接`String`值。为测试函数增加一个自定义失败信息参数:带`{}`占位符的格式字符串,以及`greeting`函数的值。
点我展开示例代码 @@ -280,7 +298,7 @@ error: test failed ``` - ```rust + ```text ---- tests::greeting_contains_name stdout ---- thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain name, value was `Hello!`', src/lib.rs:12:9 @@ -292,7 +310,9 @@ error: test failed #### 1.2.3 `should_panic` 属性 -检查代码是否按照期望处理错误。这个属性在函数中的代码 panic 时会通过,没有 panic 时失败。`should_panic`  测试结果只是告诉我们代码产生了 panic, 甚至在一些不是我们期望的原因而导致 panic 时也会通过。可选的  `expected`  参数使  `should_panic`  测试结果更精确, 确保错误信息中包含其提供的内容, 也就是说如果触发了 panic 但是 panic 的文字不包含 expected 参数里面的内容时测试仍然失败。 +检查代码是否按照期望处理错误。这个属性在函数中的代码 panic 时会通过,没有 panic 时失败。`should_panic`测试结果只是告诉我们代码产生了 +panic, 甚至在一些不是我们期望的原因而导致 panic 时也会通过。可选的`expected`参数使`should_panic`测试结果更精确, +确保错误信息中包含其提供的内容, 也就是说如果触发了 panic 但是 panic 的文字不包含 expected 参数里面的内容时测试仍然失败。
点我展开示例代码 @@ -326,22 +346,22 @@ mod tests { ``` -```rust +```shell $ cargo test - Compiling adder v0.1.0 (/Users/panwei/Desktop/code/Youtube/Rust/projects/11test/project/adder) - Finished test [unoptimized + debuginfo] target(s) in 0.32s - Running unittests src/lib.rs (target/debug/deps/adder-0980e06b8dfc4e08) +Compiling adder v0.1.0 ( / Users/panwei/Desktop/code/Youtube/Rust/projects/11test / project/adder) +Finished test [unoptimized + debuginfo] target(s) in 0.32s +Running unittests src/lib.rs (target/debug/deps/adder-0980e06b8dfc4e08) running 1 test test tests::greater_than_100 - should panic ... FAILED failures: ----- tests::greater_than_100 stdout ---- +- - - - tests::greater_than_100 stdout - - - - note: test did not panic as expected failures: - tests::greater_than_100 +tests::greater_than_100 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s @@ -379,38 +399,40 @@ mod tests { ``` -```rust +```shell $ cargo test - Compiling adder v0.1.0 (/Users/panwei/Desktop/code/Youtube/Rust/projects/11test/project/adder) - Finished test [unoptimized + debuginfo] target(s) in 6.26s - Running unittests src/lib.rs (target/debug/deps/adder-0980e06b8dfc4e08) +Compiling adder v0.1.0 ( / Users/panwei/Desktop/code/Youtube/Rust/projects/11test / project/adder) +Finished test [unoptimized + debuginfo] target(s) in 6.26s +Running unittests src/lib.rs (target/debug/deps/adder-0980e06b8dfc4e08) running 1 test test tests::greater_than_100 - should panic ... FAILED failures: ----- tests::greater_than_100 stdout ---- -thread 'tests::greater_than_100' panicked at src/lib.rs:9:13: +- - - - tests::greater_than_100 stdout - - - - +thread 'tests::greater_than_100' panicked at src/lib.rs:9: 13: Guess value must be greater than or equal to 1, got 0. note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: panic did not contain expected string - panic message: `"Guess value must be greater than or equal to 1, got 0."`, - expected substring: `"Guess value must be less than or equal to 100"` +panic message: `"Guess value must be greater than or equal to 1, got 0."`, +expected substring: `"Guess value must be less than or equal to 100"` failures: - tests::greater_than_100 +tests::greater_than_100 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s -error: test failed, to rerun pass `--lib` +error: test failed, to rerun pass ` - - lib` ```
#### 1.2.4 `Result`枚举 -前面测试运行失败都是触发了 panic, 还可以使用 `Result` 作为测试函数的返回类型来实现测试失败的目的。 不同于调用  `assert_eq!`  宏,`Result`是返回 Ok, 测试通过, 返回 Err, 测试失败。不能在使用  `Result`  的测试中使用  `#[should_panic]`  注解, 因为在测试失败时会返回 Err, 不会触发 panic。 +前面测试运行失败都是触发了 panic, 还可以使用 `Result` 作为测试函数的返回类型来实现测试失败的目的。 不同于调用 +`assert_eq!`宏,`Result`是返回 Ok, 测试通过, 返回 Err, 测试失败。不能在使用`Result`的测试中使用 +`#[should_panic]`注解, 因为在测试失败时会返回 Err, 不会触发 panic。
点我展开示例代码 @@ -430,12 +452,12 @@ mod tests { ``` -```rust +```shell $ cargo test -Compiling adder v0.1.0 (/Users/panwei/Desktop/code/Youtube/Rust/projects/11test/project/adder) - Finished test [unoptimized + debuginfo] target(s) in 1.46s - Running unittests src/lib.rs (target/debug/deps/adder-0980e06b8dfc4e08) +Compiling adder v0.1.0 ( / Users/panwei/Desktop/code/Youtube/Rust/projects/11test / project/adder) +Finished test [unoptimized + debuginfo] target(s) in 1.46s +Running unittests src/lib.rs (target/debug/deps/adder-0980e06b8dfc4e08) running 1 test test tests::it_works ... ok @@ -454,32 +476,37 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini ## 2. 控制测试的运行: 测试命令 `cargo test` 的参数 -`cargo run`  会编译代码并运行生成的二进制文件, `cargo test`  也会在测试模式下编译代码并运行生成的测试二进制文件。可以通过添加命令行参数来控制 `cargo test` 测试的运行, 将一部分命令行参数传递给  `cargo test`,接着是分隔符  `--`,再将另外一部分传递给生成的测试二进制文件。运行`cargo test --help`  会提示  `cargo test`  的有关参数,而运行  `cargo test -- --help`  可以提示在分隔符  `--`  之后使用的有关参数。 +`cargo run`会编译代码并运行生成的二进制文件, `cargo test`也会在测试模式下编译代码并运行生成的测试二进制文件。可以通过添加命令行参数来控制 +`cargo test` 测试的运行, 将一部分命令行参数传递给`cargo test`,接着是分隔符`--`,再将另外一部分传递给生成的测试二进制文件。运行 +`cargo test --help`会提示`cargo test`的有关参数,而运行`cargo test -- --help`可以提示在分隔符`--`之后使用的有关参数。
点我展开示例代码 -```rust -$ cargo test --help -$ cargo test -- --help +```shell +$ cargo test - - help +$ cargo test - - - - help ```
### 2.1 并行或连续的运行测试: `--test-threads` -当运行多个测试时, Rust 默认使用线程来并行运行, 因为运行更快。并行运行需要确保每一个测试读写不同的文件, 测试不相互依赖,或不依赖任何共享的状态,否则一个测试可能会在另一个测试读写文件过程中修改了文件, 导致相互干扰。如果不希望测试并行运行,或者想要更加精确的控制线程的数量,可以传递  `--test-threads`  参数和希望使用线程的数量给测试二进制文件。 +当运行多个测试时, Rust 默认使用线程来并行运行, 因为运行更快。并行运行需要确保每一个测试读写不同的文件, +测试不相互依赖,或不依赖任何共享的状态,否则一个测试可能会在另一个测试读写文件过程中修改了文件, +导致相互干扰。如果不希望测试并行运行,或者想要更加精确的控制线程的数量,可以传递`--test-threads`参数和希望使用线程的数量给测试二进制文件。
点我展开示例代码 -```rust -$ cargo test -- --test-threads=1 +```shell +$ cargo test - - - - test-threads=1 ```
### 2.2 显示函数的输出: `--show-output` -Rust 默认捕获(不显示)所有输出, 比如 `println!`这类宏的输出在测试通过时不显示, 测试失败时才显示所有标准输出和其他错误信息。通过在末尾增加  `--show-output`  参数来告知 Rust 显示通过测试的输出。 +Rust 默认捕获(不显示)所有输出, 比如 `println!`这类宏的输出在测试通过时不显示, 测试失败时才显示所有标准输出和其他错误信息。通过在末尾增加 +`--show-output`参数来告知 Rust 显示通过测试的输出。
点我展开示例代码 @@ -507,19 +534,20 @@ mod tests { } ``` -```rust -$ cargo test -- --show-output +```shell +$ cargo test - - - - show-output ```
### 2.3 运行部分测试: 单个测试、多个测试 -通过向  `cargo test`  传递测试名称的参数来选择运行哪些测试。 +通过向`cargo test`传递测试名称的参数来选择运行哪些测试。 -运行单个测试: 通过向  `cargo test`  传递任意测试的名称来只运行这个测试,  `2 filtered out`  表明 2 个测试被过滤掉了。 +运行单个测试: 通过向`cargo test`传递任意测试的名称来只运行这个测试,`2 filtered out`表明 2 个测试被过滤掉了。 -运行多个测试: 指定部分测试的名称,任何匹配这个名称的测试会被运行, 可以通过模块名来运行一个模块中的所有测试。比如, 运行了所有名字中带有  `add`  的测试。 +运行多个测试: 指定部分测试的名称,任何匹配这个名称的测试会被运行, 可以通过模块名来运行一个模块中的所有测试。比如, +运行了所有名字中带有`add`的测试。
点我展开示例代码 @@ -549,11 +577,11 @@ mod tests { } ``` -```rust +```shell // 运行单个测试 $ cargo test one_hundred - Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs - Running target/debug/deps/adder-06a75b4a1f2515e9 +Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs +Running target/debug/deps/adder-06a75b4a1f2515e9 running 1 test test tests::one_hundred ... ok @@ -562,11 +590,11 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out ``` -```rust +```shell // 运行多个测试 $ cargo test add - Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs - Running target/debug/deps/adder-06a75b4a1f2515e9 +Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs +Running target/debug/deps/adder-06a75b4a1f2515e9 running 2 tests test tests::add_two_and_two ... ok @@ -579,9 +607,9 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out ### 2.4 忽略部分测试: `#[ignore]` 、`--ignored` -对于不想运行的测试,可以在  `#[test]`  之后增加  `#[ignore]` 。`expensive_test`  被列为  `ignored`,没有运行。 +对于不想运行的测试,可以在`#[test]`之后增加`#[ignore]`。`expensive_test`被列为`ignored`,没有运行。 -如果只希望运行被忽略的测试,可以使用  `cargo test -- --ignored`。 +如果只希望运行被忽略的测试,可以使用`cargo test -- --ignored`。
点我展开示例代码 @@ -599,11 +627,11 @@ fn expensive_test() { } ``` -```rust +```shell $ cargo test - Compiling adder v0.1.0 (file:///projects/adder) - Finished dev [unoptimized + debuginfo] target(s) in 0.24 secs - Running target/debug/deps/adder-ce99bcc2479f4607 +Compiling adder v0.1.0 (file:///projects/adder) +Finished dev [unoptimized + debuginfo] target(s) in 0.24 secs +Running target/debug/deps/adder-ce99bcc2479f4607 running 2 tests test expensive_test ... ignored @@ -612,10 +640,10 @@ test it_works ... ok test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out ``` -```rust -$ cargo test -- --ignored - Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs - Running target/debug/deps/adder-ce99bcc2479f4607 +```shell +$ cargo test - - - - ignored +Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs +Running target/debug/deps/adder-ce99bcc2479f4607 running 1 test test expensive_test ... ok @@ -627,13 +655,19 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out ## 3. 测试的种类: 单元测试、集成测试 -- 单元测试更小更集中,在隔离的环境中一次测试一个模块,或者测试私有接口, 与被测试的代码放在 *src*  目录下的相同文件中, 并分别使用`tests`  和  `cfg(test)` 属性标注函数和模块, 前面介绍的就是单元测试。 -- 集成测试完全位于被测试库的外部, 调用方式与其他使用这个库的代码一样,只测试公有接口而且每个测试可能会测试多个模块, 需要创建一个 *tests*  目录, 与被测试代码位于不同的文件夹, 只有运行 `cargo test` 时才会编译 tests 目录下的文件, 所以不需要  `#[cfg(test)]`  标注。集成测试的目的是检查被测试库的多个部分是否能一起正常运行, 因为一些单独能正确运行的代码单元集成在一起也可能会出现问题,所以集成测试的覆盖率也很重要。 +- 单元测试更小更集中,在隔离的环境中一次测试一个模块,或者测试私有接口, 与被测试的代码放在 *src*目录下的相同文件中, 并分别使用 + `tests`和`cfg(test)` 属性标注函数和模块, 前面介绍的就是单元测试。 +- 集成测试完全位于被测试库的外部, 调用方式与其他使用这个库的代码一样,只测试公有接口而且每个测试可能会测试多个模块, + 需要创建一个 *tests*目录, 与被测试代码位于不同的文件夹, 只有运行 `cargo test` 时才会编译 tests 目录下的文件, 所以不需要 + `#[cfg(test)]`标注。集成测试的目的是检查被测试库的多个部分是否能一起正常运行, + 因为一些单独能正确运行的代码单元集成在一起也可能会出现问题,所以集成测试的覆盖率也很重要。 ### 3.1 单元测试 Uint Tests - 测试模块和 `#[cfg(test)]` - 创建项目会自动生成的测试模块。`cfg`  属性代表  *configuration(配置)* ,告诉 Rust 下面的代码只被包含在特定的配置选项中, 这里的配置选项是用来编译和运行测试的  `test`,所以测试模块的  `#[cfg(test)]`  标注告诉 Rust 只在执行  `cargo test`  时才编译和运行模块中的 `helper` 函数和 `#[test]` 标注的函数,而在运行  `cargo build`  时它们不应该被包含进编译结果中。 + 创建项目会自动生成的测试模块。`cfg`属性代表*configuration(配置)*,告诉 Rust 下面的代码只被包含在特定的配置选项中, + 这里的配置选项是用来编译和运行测试的`test`,所以测试模块的`#[cfg(test)]`标注告诉 Rust 只在执行`cargo test`时才编译和运行模块中的 + `helper` 函数和 `#[test]` 标注的函数,而在运行`cargo build`时它们不应该被包含进编译结果中。
点我展开示例代码 @@ -681,8 +715,8 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out #### 3.2.1 tests 目录 -为了编写集成测试,需要在项目根目录创建一个  *tests*  目录,与  *src*  同级。接着可以随意在这个目录中创建任意多的测试文件。 -保留测试私有函数中  *src/lib.rs*  的代码。并创建一个  *tests*  目录,新建一个文件  *tests/integration_test.rs*,如下所示: +为了编写集成测试,需要在项目根目录创建一个*tests*目录,与*src*同级。接着可以随意在这个目录中创建任意多的测试文件。 +保留测试私有函数中*src/lib.rs*的代码。并创建一个*tests*目录,新建一个文件*tests/integration_test.rs*,如下所示:
点我展开示例代码 @@ -697,35 +731,36 @@ fn it_adds_two() {
-运行  `cargo test`  后, 得到三个部分的输出:单元测试、集成测试和文档测试。 +运行`cargo test`后, 得到三个部分的输出:单元测试、集成测试和文档测试。 第一部分单元测试与之前的一样:每个单元测试一行,接着是一个单元测试的摘要行。 -集成测试部分以行  `Running tests/integration_test.rs (target/debug/deps/integration-test-ce99bcc2479f4607)`(在输出最后的哈希值可能不同)开头。接下来每一行是一个集成测试中的测试函数,以及一个位于  `Doc-tests adder`  部分之前的集成测试的摘要行。 +集成测试部分以行`Running tests/integration_test.rs (target/debug/deps/integration-test-ce99bcc2479f4607)` +(在输出最后的哈希值可能不同)开头。接下来每一行是一个集成测试中的测试函数,以及一个位于`Doc-tests adder`部分之前的集成测试的摘要行。
点我展开示例代码 -```solidity +```shell $ cargo test - Compiling adder v0.1.0 (file:///projects/adder) - Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs - Running target/debug/deps/adder-abcabcabc +Compiling adder v0.1.0 (file : ///projects/adder) +Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs +Running target / debug / deps/ adder - abcabcabc running 1 test -test tests::internal ... ok +test tests :: internal ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result : ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Running target/debug/deps/integration_test-ce99bcc2479f4607 +Running target / debug/ deps / integration_test - ce99bcc2479f4607 running 1 test test it_adds_two ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result : ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Doc-tests adder +Doc - tests adder running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result : ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ```
@@ -737,10 +772,10 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
点我展开示例代码 -```rust -$ cargo test --test integration_test - Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs - Running target/debug/integration_test-952a27e0126bb565 +```shell +$ cargo test - - test integration_test +Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs +Running target/debug/integration_test-952a27e0126bb565 running 1 test test it_adds_two ... ok @@ -752,11 +787,13 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out #### 3.2.2 共享子模块 -Cargo 会将每一个文件当作单独的 crate 来编译, 这些文件不共享行为 (与 src 下的文件规则不同), 所以需要在每一个文件中导入被测试库 `use adder` 。但是, 如果想创建一个 `helper` 帮助函数, 比如创建一个 *tests/common.rs*  文件和一个名叫  `setup`  的函数,并希望这个函数能被多个测试文件的测试函数调用。 +Cargo 会将每一个文件当作单独的 crate 来编译, 这些文件不共享行为 (与 src 下的文件规则不同), 所以需要在每一个文件中导入被测试库 +`use adder` 。但是, 如果想创建一个 `helper` 帮助函数, 比如创建一个 *tests/common.rs*文件和一个名叫`setup` +的函数,并希望这个函数能被多个测试文件的测试函数调用。
点我展开示例代码 -```solidity +```rust pub fn setup() { // 编写特定库测试所需的代码 } @@ -764,30 +801,31 @@ pub fn setup() {
-再次运行测试,将会在测试结果中看到一个新的对应  `common.rs`  文件的测试结果部分,即便这个文件并没有包含任何测试函数,也没有任何地方调用了  `seup`  函数。 +再次运行测试,将会在测试结果中看到一个新的对应`common.rs`文件的测试结果部分,即便这个文件并没有包含任何测试函数,也没有任何地方调用了 +`setup`函数。
点我展开示例代码 -```rust +```shell running 1 test test tests::internal ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Running target/debug/deps/common-b8b07b6f1be2db70 +Running target/debug/deps/common-b8b07b6f1be2db70 running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Running target/debug/deps/integration_test-d993c68b431d39df +Running target/debug/deps/integration_test-d993c68b431d39df running 1 test test it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Doc-tests adder +Doc-tests adder running 0 tests @@ -796,11 +834,14 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
-如果, 不希望  `common`  出现在测试输出中,就需要创建  *`tests/common/mod.rs`* ,而不是创建  *`tests/common.rs`* 。*`tests`*  目录中的子目录不会被作为单独的 `crate` 编译或作为一个测试结果部分出现在测试输出中。然后, 可以将 _`tests/common/mod.rs`_ 作为模块在任何集成测试文件中使用。比如在 *`tests/integration_test.rs`*  中的  `it_adds_two`  测试, 声明模块 `mod common` , 调用  `setup`  函数 `common::setup()`。 +如果, 不希望`common`出现在测试输出中,就需要创建*`tests/common/mod.rs`*,而不是创建*`tests/common.rs`*。*`tests`* +目录中的子目录不会被作为单独的 `crate` 编译或作为一个测试结果部分出现在测试输出中。然后, 可以将 _`tests/common/mod.rs`_ +作为模块在任何集成测试文件中使用。比如在 *`tests/integration_test.rs`*中的`it_adds_two`测试, 声明模块 `mod common` , 调用 +`setup`函数 `common::setup()`。
点我展开示例代码 -```solidity +```rust use adder; mod common; @@ -808,7 +849,7 @@ mod common; #[test] fn it_adds_two2() { common::setup(); - assert_eq!(4, adder::add_two(2)); + assert_eq!(4, adder : : add_two(2)); } ``` @@ -816,8 +857,11 @@ fn it_adds_two2() { #### 3.2.3 二进制 binary crate -如果项目是二进制 binary crate 并且只包含  src/main.rs  而没有  src/lib.rs,这样就不可能在  tests  目录创建集成测试并使用  use  语句导入  src/main.rs  中定义的函数。只有库 library crate 才会向其他 crate 暴露可供调用和使用的函数, binary crate 是独立运行, 并且只会把核心逻辑代码即需要测试的代码放在 src/lib.rs 里。 +如果项目是二进制 binary crate 并且只包含 src/main.rs 而没有 src/lib.rs,这样就不可能在 tests 目录创建集成测试并使用 use +语句导入 src/main.rs 中定义的函数。只有库 library crate 才会向其他 crate 暴露可供调用和使用的函数, binary crate 是独立运行, +并且只会把核心逻辑代码即需要测试的代码放在 src/lib.rs 里。 ## 总结 -本文涵盖 Rust 测试常用函数 (assert!, panic! 等常用的宏、属性和枚举), 命令 (cargo test) 及种类 (单元测试、集成测试), 助你快速上手 Rust 测试。 +本文涵盖 Rust 测试常用函数 (assert!, panic! 等常用的宏、属性和枚举), 命令 (cargo test) 及种类 (单元测试、集成测试), 助你快速上手 +Rust 测试。 diff --git a/07_Rust_Tests/adder/Cargo.toml b/07_Tests/adder/Cargo.toml similarity index 85% rename from 07_Rust_Tests/adder/Cargo.toml rename to 07_Tests/adder/Cargo.toml index 7f9060f..0d5c12d 100644 --- a/07_Rust_Tests/adder/Cargo.toml +++ b/07_Tests/adder/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" [lib] path = "src/lib.rs" -[dependencies] \ No newline at end of file +[dependencies] diff --git a/07_Rust_Tests/adder/src/lib.rs b/07_Tests/adder/src/lib.rs similarity index 73% rename from 07_Rust_Tests/adder/src/lib.rs rename to 07_Tests/adder/src/lib.rs index 8d9653a..fbb208d 100644 --- a/07_Rust_Tests/adder/src/lib.rs +++ b/07_Tests/adder/src/lib.rs @@ -19,33 +19,37 @@ pub fn add_two(a: i32) -> i32 { a + 2 } -// pub fn greeting(name: &str) -> String { -// format!("Hello {}!", name) -// } +pub fn greeting_one(name: &str) -> String { + format!("Hello {}!", name) +} -pub fn greeting(name: &str) -> String { +pub fn greeting_two(name: &str) -> String { String::from("Hello!") } -pub struct Guess { +pub struct GuessOne { value: i32, } -// impl Guess { -// pub fn new(value: i32) -> Guess { -// if value < 1 { -// panic!("Guess value must be between 1 and 100, got {}.", value); -// } +impl GuessOne { + pub fn new(value: i32) -> GuessOne { + if value < 1 { + panic!("Guess value must be between 1 and 100, got {}.", value); + } + + GuessOne { + value + } + } +} -// Guess { -// value -// } -// } -// } +pub struct GuessTwo { + value: i32, +} -impl Guess { - pub fn new(value: i32) -> Guess { +impl GuessTwo { + pub fn new(value: i32) -> GuessTwo { if value < 1 { panic!("Guess value must be greater than or equal to 1, got {}.", value); @@ -54,7 +58,7 @@ impl Guess { value); } - Guess { + GuessTwo { value } } @@ -109,31 +113,31 @@ mod tests { assert_eq!(4, add_two(2)); } - // #[test] - // fn greeting_contains_name() { - // let result = greeting("Carol"); - // assert!(result.contains("Carol")); - // } + #[test] + fn greeting_contains_name_one() { + let result = greeting_one("Carol"); + assert!(result.contains("Carol")); + } #[test] - fn greeting_contains_name() { - let result = greeting("Carol"); - assert!( - result.contains("Carol"), - "Greeting did not contain name, value was `{}`", result + fn greeting_contains_name_two() { + let result = greeting_two("Carol"); + assert!( + result.contains("Carol"), + "Greeting did not contain name, value was `{}`", result ); } - - // #[test] - // #[should_panic] - // fn greater_than_100() { - // Guess::new(200); - // } - + + #[test] + #[should_panic] + fn greater_than_100_one() { + GuessOne::new(200); + } + #[test] #[should_panic(expected = "Guess value must be less than or equal to 100")] - fn greater_than_100() { - Guess::new(0); + fn greater_than_100_two() { + GuessTwo::new(0); } #[test] @@ -182,12 +186,5 @@ mod tests { fn internal() { assert_eq!(4, internal_adder(2, 2)); } - } - - - - - - diff --git a/07_Rust_Tests/adder/tests/common/mod.rs b/07_Tests/adder/tests/common/mod.rs similarity index 96% rename from 07_Rust_Tests/adder/tests/common/mod.rs rename to 07_Tests/adder/tests/common/mod.rs index 8b41779..ebd6914 100644 --- a/07_Rust_Tests/adder/tests/common/mod.rs +++ b/07_Tests/adder/tests/common/mod.rs @@ -1,3 +1,3 @@ pub fn setup() { // 编写特定库测试所需的代码 -} \ No newline at end of file +} diff --git a/07_Rust_Tests/adder/tests/integration_test.rs b/07_Tests/adder/tests/integration_test.rs similarity index 100% rename from 07_Rust_Tests/adder/tests/integration_test.rs rename to 07_Tests/adder/tests/integration_test.rs From 08d11fd64b23845913ecfc55e735be168b13191b Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Mon, 30 Sep 2024 17:42:20 +0800 Subject: [PATCH 16/18] format code --- 12_Lifetime/README.md | 48 +++++++++++++++++++++++++++++------------ 12_Lifetime/src/main.rs | 28 ++++++++++++++++-------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/12_Lifetime/README.md b/12_Lifetime/README.md index 610f4d7..9e05567 100644 --- a/12_Lifetime/README.md +++ b/12_Lifetime/README.md @@ -1,18 +1,20 @@ --- title: Lifetime tags: -- Rust -- basic -- wtfacademy + - Rust + - basic + - wtfacademy --- # WTF Rust 极简入门: 生命周期 -在 Rust 中,生命周期是一个非常重要的概念,用于确保引用不会悬空,即引用的数据在引用存在的时间里始终有效。Rust 编译器通过生命周期来检查这种有效性。这一节将讨论生命周期的重要性、如何使用生命周期注解,以及它如何帮助我们写出更安全的代码。 +在 Rust 中,生命周期是一个非常重要的概念,用于确保引用不会悬空,即引用的数据在引用存在的时间里始终有效。Rust +编译器通过生命周期来检查这种有效性。这一节将讨论生命周期的重要性、如何使用生命周期注解,以及它如何帮助我们写出更安全的代码。 ## 悬空引用 -在第 5 节我们知道,Rust 中所有的对象都是有主人(owner)的,它拥有对象的所有权(ownership),所有权具有唯一性,但很多时候我们并不需要对象的所有权,有使用权就够了,而使用权在 Rust 中称之为引用(borrow),或者说借用。 +在第 5 节我们知道,Rust 中所有的对象都是有主人(owner)的,它拥有对象的所有权(ownership),所有权具有唯一性,但很多时候我们并不需要对象的所有权,有使用权就够了,而使用权在 +Rust 中称之为引用(borrow),或者说借用。 ``` let s1 = String::from("hello"); @@ -30,21 +32,27 @@ let s2; println!("s2: {}", s2); ``` -上面的例子中,`s2` 引用的 `s1` 对象的作用域在花括号内部,超出这个作用域之后,`s1` 这个对象会被 Rust 自动回收,此时再去打印 `s2` 的值就会发生错误:`s1 does not live long enough, borrowed value does not live long enough`,即对象 `s1` 存活的时间不够长,导致 `s2` 引用了一个不存在的值,这种情况,我们称之为**悬空引用**。 +上面的例子中,`s2` 引用的 `s1` 对象的作用域在花括号内部,超出这个作用域之后,`s1` 这个对象会被 Rust 自动回收,此时再去打印 +`s2` 的值就会发生错误:`s1 does not live long enough, borrowed value does not live long enough`,即对象 `s1` 存活的时间不够长,导致 +`s2` 引用了一个不存在的值,这种情况,我们称之为**悬空引用**。 ## 生命周期基础 -Rust 中的每一个引用都有其生命周期(lifetime),也就是引用保持有效的作用域。生命周期在 Rust 中的核心作用是防止悬空引用的发生,它是许多程序错误和安全隐患的根源。生命周期确保内存安全,无需垃圾收集。 +Rust 中的每一个引用都有其生命周期(lifetime),也就是引用保持有效的作用域。生命周期在 Rust +中的核心作用是防止悬空引用的发生,它是许多程序错误和安全隐患的根源。生命周期确保内存安全,无需垃圾收集。 在大多数时候,我们无需手动的声明生命周期,因为编译器可以自动进行推导,但当多个生命周期存在时,编译器可能无法进行引用的生命周期分析,就需要我们手动标明不同引用之间的生命周期关系,也就是生命周期注解。 ### 生命周期注解语法 -生命周期参数名称必须以撇号`'`开头,其名称通常全是小写,类似于泛型其名称非常短。`'a` 是默认使用的名称。生命周期参数标注位于引用符 `&` 之后,并有一个空格来将生命周期注解与引用的类型分隔开,如 `&'a i32`。 +生命周期参数名称必须以撇号`'`开头,其名称通常全是小写,类似于泛型其名称非常短。`'a` 是默认使用的名称。生命周期参数标注位于引用符 +`&` 之后,并有一个空格来将生命周期注解与引用的类型分隔开,如 `&'a i32`。 生命周期注解并不改变任何引用的生命周期的长短。它只是描述了多个引用生命周期相互的关系,便于编译器进行引用的分析,但不影响其生命周期。 -生命周期即 Rust 中值的生老病死,而生命周期注解就是用于约定多个引用之间生死的关系,就像桃园三结义中誓约的那样:不求同年同月同日生,但求同年同月同日死,这三兄弟之间的誓约就如同 Rust 中的生命周期注解。誓约是为了防止有人背信弃义,而 Rust 生命周期注解是为了编译器进行分析,防止出现悬垂引用。有了誓约并不代表大家就一定一起赴死,就如同生命周期注解并不改变值的生命周期一样。 +生命周期即 Rust 中值的生老病死,而生命周期注解就是用于约定多个引用之间生死的关系,就像桃园三结义中誓约的那样:不求同年同月同日生,但求同年同月同日死,这三兄弟之间的誓约就如同 +Rust 中的生命周期注解。誓约是为了防止有人背信弃义,而 Rust +生命周期注解是为了编译器进行分析,防止出现悬空引用。有了誓约并不代表大家就一定一起赴死,就如同生命周期注解并不改变值的生命周期一样。 ```rust fn borrow<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { @@ -95,7 +103,9 @@ fn longest<'a, 'b: 'a>(x: &'a str, y: &'b str) -> &'a str { } ``` -这个函数 `longest` 涉及到了两个不同的生命周期注解 `'a` 和 `'b`,而 `'b: 'a` 表示输入参数 `y` 的生命周期至少与输入参数 `x` 相同,或比它更长。函数返回值中 `'a` 表示返回的引用将具有与输入参数 `x` 相同的生命周期,**也就是生命周期中最小的那个**。这样可能比较抽象,我们看下具体的例子: +这个函数 `longest` 涉及到了两个不同的生命周期注解 `'a` 和 `'b`,而 `'b: 'a` 表示输入参数 `y` 的生命周期至少与输入参数 `x` +相同,或比它更长。函数返回值中 `'a` 表示返回的引用将具有与输入参数 `x` 相同的生命周期,**也就是生命周期中最小的那个** +。这样可能比较抽象,我们看下具体的例子: ```rust fn main() { @@ -107,9 +117,13 @@ fn main() { } } ``` -上面的代码展示了 `string1` 和 `string2` 这两个不同生命周期的变量,前者的生命周期位于外部的 `{}` 中,而后者的生命周期位于内部的 `{}` 中,所以 `'a` 代表的生命周期范围是两者中较小的那个,即内部的 `{}`,此时返回值 `result` 的生命周期也是属于内部的 `{}`,即返回值能够保证在 `string1` 和 `string2` 中较短的那个生命周期结束前有效,此时不会发生悬垂引用,编译通过。 + +上面的代码展示了 `string1` 和 `string2` 这两个不同生命周期的变量,前者的生命周期位于外部的 `{}` 中,而后者的生命周期位于内部的 +`{}` 中,所以 `'a` 代表的生命周期范围是两者中较小的那个,即内部的 `{}`,此时返回值 `result` 的生命周期也是属于内部的 `{}` +,即返回值能够保证在 `string1` 和 `string2` 中较短的那个生命周期结束前有效,此时不会发生悬空引用,编译通过。 接下来,让我们尝试另外一个例子,该例子揭示了 `result` 的引用的生命周期必须是两个参数中较短的那个。 + ```rust fn main() { let string1 = String::from("abcdefghijklmnopqrstuvwxyz"); @@ -121,10 +135,16 @@ fn main() { println!("The longest string is {}", result); } ``` -此时 `'a` 代表的生命周期范围依旧是变量 `string1` 和 `string2` 中最小的那个,即内部的 `{}`,但返回值 `result` 的生命周期范围却是外部的 `{}`,而不是内部的 `{}`,也就意味着 `result` 可能会引用一个无效的值,因此编译失败。 -> 注意:通过人为观察 `result` 的引用应该为 `string1`,这样返回值 `result` 和 `string1` 的作用域是一致的,理论上是应该编译通过的。但是,Rust 的编译器会采用保守的策略,我们通过生命周期标注告诉 Rust,`longest` 函数返回值的生命周期是传入参数中较小的那个变量的生命周期,因此 Rust 编译器不允许上述代码通过,因为可能存在无效引用。 +此时 `'a` 代表的生命周期范围依旧是变量 `string1` 和 `string2` 中最小的那个,即内部的 `{}`,但返回值 `result` 的生命周期范围却是外部的 +`{}`,而不是内部的 `{}`,也就意味着 `result` 可能会引用一个无效的值,因此编译失败。 + +> 注意:通过人为观察 `result` 的引用应该为 `string1`,这样返回值 `result` 和 `string1` 的作用域是一致的,理论上是应该编译通过的。但是,Rust +> 的编译器会采用保守的策略,我们通过生命周期标注告诉 Rust,`longest` 函数返回值的生命周期是传入参数中较小的那个变量的生命周期,因此 +> Rust 编译器不允许上述代码通过,因为可能存在无效引用。 ## 总结 -理解和正确使用生命周期是掌握 Rust 的重要部分。生命周期注解帮助 Rust 编译器保证引用的有效性,从而让你的程序在处理引用时更加安全。虽然开始时可能会觉得生命周期有些复杂,但随着实践的深入,你会逐渐领会它们的重要性和用法。掌握生命周期让你能写出更健壮、安全的 Rust 代码。如果你有任何问题或需要更多例子,请随时提问! \ No newline at end of file +理解和正确使用生命周期是掌握 Rust 的重要部分。生命周期注解帮助 Rust +编译器保证引用的有效性,从而让你的程序在处理引用时更加安全。虽然开始时可能会觉得生命周期有些复杂,但随着实践的深入,你会逐渐领会它们的重要性和用法。掌握生命周期让你能写出更健壮、安全的 +Rust 代码。如果你有任何问题或需要更多例子,请随时提问! \ No newline at end of file diff --git a/12_Lifetime/src/main.rs b/12_Lifetime/src/main.rs index 01e7c6c..61ff626 100644 --- a/12_Lifetime/src/main.rs +++ b/12_Lifetime/src/main.rs @@ -1,4 +1,3 @@ - fn borrow<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { if x > y { x } else { y } } @@ -46,15 +45,26 @@ fn main() { */ - let string1 = String::from("abcdefghijklmnopqrstuvwxyz"); - let result; - { - let string2 = String::from("123456789"); - result = longest(string1.as_str(), string2.as_str()); - println!("The longest string is {}", result); - } + let string1 = String::from("abcdefghijklmnopqrstuvwxyz"); + let result1; + { + let string2 = String::from("123456789"); + result1 = longest(string1.as_str(), string2.as_str()); + println!("The longest string is {}", result1); + } - // `string2` does not live long enough,borrowed value does not live long enough + // `string2` does not live long enough,borrowed value does not live long enough // println!("The longest string is {}", result); + + let string3 = String::from("abcdefghijklmnopqrstuvwxyz"); + let result2; + { + let string4 = String::from("123456789"); + result2 = longest(string4.as_str(), string3.as_str()); + println!("The longest string is {}", result2); + } + + // `string4` does not live long enough,borrowed value does not live long enough + // println!("The longest string is {}", result2); } From c62a638fbab0b039f9283c4bd99e0ff47e26c733 Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Mon, 30 Sep 2024 17:42:29 +0800 Subject: [PATCH 17/18] format code --- README.md | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 443d1e0..a5d4205 100644 --- a/README.md +++ b/README.md @@ -4,46 +4,61 @@ > 我们最近在学习 Rust,巩固一下细节,也写一个“WTF Rust 极简入门”,供小白们使用(编程大佬可以另找教程)。 -**第 1 讲 Hello Rust**:[Code](01_HelloRust/hello_cargo/src/main.rs) | [教程](01_HelloRust/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 1 讲 Hello Rust**:[Code](01_HelloRust/hello_cargo/src/main.rs) | [教程](01_HelloRust/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 2 讲 基本类型**:[Code](02_BaseType/src/main.rs) | [教程](02_BaseType/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 2 讲 基本类型**:[Code](02_BaseType/src/main.rs) | [教程](02_BaseType/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 3 讲 复合类型**:[Code](03_CompoundType/src/main.rs) | [教程](03_CompoundType/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 3 讲 复合类型**:[Code](03_CompoundType/src/main.rs) | [教程](03_CompoundType/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 4 讲 结构体,枚举**:[Code](04_Struct_Enum/src/main.rs) | [教程](04_Struct_Enum/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 4 讲 结构体,枚举**:[Code](04_Struct_Enum/src/main.rs) | [教程](04_Struct_Enum/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 5 讲 所有权、借用、引用**:[Code](05_Ownership/src/main.rs) | [教程](05_Ownership/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 5 讲 所有权、借用、引用**:[Code](05_Ownership/src/main.rs) | [教程](05_Ownership/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 6 讲 函数**:[Code](06_Function/src/main.rs) | [教程](06_Function/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 6 讲 函数**:[Code](06_Function/src/main.rs) | [教程](06_Function/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 7 讲 Rust 测试**:[Code](07_Rust_Tests/adder/src/lib.rs) | [教程](07_Rust_Tests/rust_tests.md) | 本文作者: [Eta](https://twitter.com/pwhattie) +**第 7 讲 Rust 测试**:[Code](07_Tests/src/lib.rs) | [教程](07_Tests/README) | +本文作者: [Eta](https://twitter.com/pwhattie) -**第 8 讲 集合类型**:[Code](08_Collections/src/main.rs) | [教程](08_Collections/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 8 讲 集合类型**:[Code](08_Collections/src/main.rs) | [教程](08_Collections/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 9 讲 错误处理**:[Code](09_Errors/src/main.rs) | [教程](09_Errors/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 9 讲 错误处理**:[Code](09_Errors/src/main.rs) | [教程](09_Errors/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 10 讲 Trait**:[Code](10_Trait/src/main.rs) | [教程](10_Trait/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 10 讲 Trait**:[Code](10_Trait/src/main.rs) | [教程](10_Trait/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 11 讲 泛型**:[Code](11_Generics/src/main.rs) | [教程](11_Generics/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 11 讲 泛型**:[Code](11_Generics/src/main.rs) | [教程](11_Generics/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 12 讲 生命周期**:[Code](12_Lifetime/src/main.rs) | [教程](12_Lifetime/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 12 讲 生命周期**:[Code](12_Lifetime/src/main.rs) | [教程](12_Lifetime/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 13 讲 Cargo**:[Code](13_Cargo/src/main.rs) | [教程](13_Cargo/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 13 讲 Cargo**:[Code](13_Cargo/src/main.rs) | [教程](13_Cargo/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 14 讲 Rust 常用库**:[Code](14_Rust_Lib/src/lib.rs) | [教程](14_Rust_Lib/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 14 讲 Rust 常用库**:[Code](14_Rust_Lib/src/lib.rs) | [教程](14_Rust_Lib/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 15 讲 线程**:[Code](15_Thread/src/main.rs) | [教程](15_Thread/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 15 讲 线程**:[Code](15_Thread/src/main.rs) | [教程](15_Thread/README.md) | +本文作者: [Carp](https://github.com/carpcai) -**第 16 讲 通道**:[Code](16_Channel/src/main.rs) | [教程](16_Channel/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 16 讲 通道**:[Code](16_Channel/src/main.rs) | [教程](16_Channel/README.md) | +本文作者: [Carp](https://github.com/carpcai) **第 17 讲 锁**:[Code](17_Lock/src/main.rs) | [教程](17_Lock/README.md) | 本文作者: [Carp](https://github.com/carpcai) -**第 18 讲 异步编程**:[Code](18_Async/src/main.rs) | [教程](18_Async/README.md) | 本文作者: [Carp](https://github.com/carpcai) +**第 18 讲 异步编程**:[Code](18_Async/src/main.rs) | [教程](18_Async/README.md) | +本文作者: [Carp](https://github.com/carpcai) **第 19 讲 IO**:[Code](19_IO/src/main.rs) | [教程](19_IO/README.md) | 本文作者: [Carp](https://github.com/carpcai) - - ## Reference - [Rust Book](https://doc.rust-lang.org/book) From 687d47a60c0aa394057c20934606bd74625ece7d Mon Sep 17 00:00:00 2001 From: simbahebinbo Date: Mon, 30 Sep 2024 17:44:37 +0800 Subject: [PATCH 18/18] format code --- {14_Rust_libs => 14_Libs}/Cargo.toml | 2 +- {14_Rust_libs => 14_Libs}/README.md | 0 {14_Rust_libs => 14_Libs}/src/main.rs | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {14_Rust_libs => 14_Libs}/Cargo.toml (94%) rename {14_Rust_libs => 14_Libs}/README.md (100%) rename {14_Rust_libs => 14_Libs}/src/main.rs (100%) diff --git a/14_Rust_libs/Cargo.toml b/14_Libs/Cargo.toml similarity index 94% rename from 14_Rust_libs/Cargo.toml rename to 14_Libs/Cargo.toml index bfaacff..bb4eafe 100644 --- a/14_Rust_libs/Cargo.toml +++ b/14_Libs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rust_libs" +name = "libs" version = "0.1.0" edition = "2021" diff --git a/14_Rust_libs/README.md b/14_Libs/README.md similarity index 100% rename from 14_Rust_libs/README.md rename to 14_Libs/README.md diff --git a/14_Rust_libs/src/main.rs b/14_Libs/src/main.rs similarity index 100% rename from 14_Rust_libs/src/main.rs rename to 14_Libs/src/main.rs