心智圖資源庫 精通RUST第2版
這是一篇關於精通RUST的心智圖,透過以上步驟和建議,您可以逐步提升自己的Rust程式設計技能,並享受Rust帶來的記憶體安全和高效能。
編輯於2024-04-04 15:14:48Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Projektmanagement ist der Prozess der Anwendung von Fachwissen, Fähigkeiten, Werkzeugen und Methoden auf die Projektaktivitäten, so dass das Projekt die festgelegten Anforderungen und Erwartungen im Rahmen der begrenzten Ressourcen erreichen oder übertreffen kann. Dieses Diagramm bietet einen umfassenden Überblick über die 8 Komponenten des Projektmanagementprozesses und kann als generische Vorlage verwendet werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Projektmanagement ist der Prozess der Anwendung von Fachwissen, Fähigkeiten, Werkzeugen und Methoden auf die Projektaktivitäten, so dass das Projekt die festgelegten Anforderungen und Erwartungen im Rahmen der begrenzten Ressourcen erreichen oder übertreffen kann. Dieses Diagramm bietet einen umfassenden Überblick über die 8 Komponenten des Projektmanagementprozesses und kann als generische Vorlage verwendet werden.
精通RUST
第1章 Rust入門
1.1 Rust是什麼,以及為何需要
Rust是一種快速、高並發、安全且具授權性的程式語言,最初由Graydon Hoare於2006年創作並發布。現在它是一種開源語言,主要由Mozilla團隊和許多開源社群成員共同維護和開發。
Rust在GitHub上有開源開發的網址,它的發展勢頭非常迅猛。透過社群驅動的請求註解流程(Request For Comment, RFC)將新功能新增至語言中,並且任何人都可以在其中提交新的功能特性,然後在RFC文件中詳細描述它們。之後就RFC尋求共識,如果達成共識,則該功能特性進入實施階段。
Rust作為一門靜態和強型別語言而存在。靜態屬性意味著編譯器在編譯時具有所有相關變數和類型的信息,並且在編譯時會進行大量檢查,在運行時只保留少量的類型檢查。
1.2 安裝Rust工具鏈
rustup.rs
1.3 Rust簡介
1.3.1 基元類型
bool
這些是常見的布林值,可以是真(true),也可以是假(false)。
char
字元
integer
此類型的特徵在於位寬。 Rust支援的最大長度是128位元。
isize
尺寸可變的有符號整形(尺寸取決於底層指針大小)。
usize
尺寸可變的無符號整形(尺寸取決於底層指針大小)。
f32
f32位元浮點型
f64
f64位元浮點型
[T; N]
固定大小的數組,T表示元素類型,N表示元素數目,並且是編譯期非負常數。
[T]
動態大小的連續序列的視圖,T表示任意型別。
str
字串切片,主要用做引用,即&str
(T, U, ..)
有限序列,T和U可以是不同型別。
fn(i32)->i32
一個接收i32型別參數並傳回i32型別參數的函數。函數也有一種類型。
1.3.2 變數宣告與不可變性
在Rust中,我們使用關鍵字let來宣告變數。初始化變數後,無法為變數分配其他值。如果稍後需要將變數指向其他變數(相同類型),則需要在其前面加上關鍵字mut。
1.3.3 函數
函數將一堆指令抽象化為具名實體,稍後可以透過其他程式碼呼叫這些指令,並幫助使用者管理複雜性。
fn add(a: u64, b: u64) -> u64 { a b } fn main() { let a: u64 = 17; let b = 3; let result = add(a, b); println!("Result {}", result); }
函數基本上是傳回值的表達式,預設是()(unit)類型的值,這與C/C 中的void回傳類型相似。
1.3.4 閉包
Rust支援閉包。閉包與函數類似,但具有聲明它們的環境或作用域的更多資訊。雖然函數具有與之關聯的名稱,閉包的定義沒有,但可以將它們指派給變數。 Rust類型推論的另一個優點是,在大多數情況下,你可以為沒有類型的閉包指定參數。這是一個最簡單的閉包"let my_closure = ||();"。我們定義了一個什麼都不做的無參數閉包。然後我們可以透過my_closure()來呼叫它,這和函數類似。兩個豎條「||」用於存放閉包參數,例如|a, b|。當Rust無法找出正確的類型時,有時需要指定參數類型(|a:u32|)。
fn main() { let doubler = |x| x * 2; let value = 5; let twice = doubler(value); println!("{} doubled is {}", value, twice); let big_closure = |b, c| { let z = b c; z * twice }; let some_number = big_closure(1, 2); println!("Result from closure: {}", some_number); }
1.3.5 字串
字串是在任何程式語言中最常用的資料類型之一。在Rust中,它們通常以兩種形式出現:&str類型和String類型。 Rust字串保證是有效的UTF-8編碼位元組序列。它們不像C字串那樣以空值(NULL)終止,並且可以在字串之間包含空的位元組。
fn main() { let question = "How are you ?"; let person: String = "Bob".to_string(); let namaste = String::from("zd"); println!("{}! {} {}", namaste, question, person); }
在上述程式碼中,person和namaste的類型是String,而question的類型為&str。建立String類型資料的方法有多種。 String類型資料是在堆疊上分配的,&str類型資料通常是指向現有字串的指針,這些字串在以在堆疊和堆上,也可以是已編譯物件代碼的資料段中的字串。
1.3.6 條件和判斷
Rust中的條件判斷和其他語言中的類似,它們也遵循類C語言風格的if else結構。
fn main() { let rust_is_awesome = true; if rust_is_awesome { println!("Indeed"); } else { println!("Well, you should try Rust !"); } }
在Rust中,if構造不是語句,而是一個表達式。在一般的程式用語中,語句不會傳回任何值,但表達式會傳回值。這種差異意味著Rust中的if else條件總是會傳回一個值。該值可以是empty類型的(),也可能是實際的值。無論花括號的最後一行是什麼,都會成為if else表達式的回傳值。重點要注意if和else分支應該有相同的回傳類型。
fn main() { let result = if 1 == 2 { "Wait, what ?" } else { "Rust makes sense" }; println!("You know what ? {}.", result); }
當將要指派的值從if else表達式傳回時,我們需要用分號作為結束標誌。例如,if是表達式,那麼let就是一個聲明,期望我們在結尾處有分號。
1.3.7 match表達式
Rust的match(匹配)表達式非常簡單、易用。它基本上類似於C語言的switch語句簡化版,允許使用者根據變數的值,以及是否有高階過濾功能做出判斷。
fn req_status() -> u32 { 200 } fn main() { let status = req_status(); match status { 200 => println!("Success"), 404 => println!("Not Found"), other => { println!("Request failed with code: {}", other); } } }
每個匹配必須傳回相同的類型。此外,我們必須對所有可能匹配的值進行徹底匹配。 Rust允許我們透過使用catch all變數(這裡是other)或_(下畫線)來忽略其餘的可能性。
1.3.8 循環
在Rust中重複做某些事情可以使用3種構造來完成,即loop、while和for。在所有這些構造中,通常都包含關鍵字continue和break,分別允許你跳過和跳出循環。
fn main() { let mut x = 1024; loop { if x < 0 { break; } println!("{} more runs to go", x); x -= 1; } }
在Rust中執行循環的一個額外特性是,能夠使用名稱標記循環程式碼區塊。這可以在你有兩個或多個巢狀循環,並且想要從它們中的任何一個中斷的情況下使用,而不僅針對直接包含break語句的循環。
fn silly_sub(a: i32, b: i32) -> i32 { let mut result = 0; 'increment: loop { if result == a { let mut dec = b; 'decrement: loop { if dec == 0 { break 'increment; } else { result -= 1; dec -= 1; } } } else { result = 1; } } result } fn main() { let a = 10; let b = 4; let result = silly_sub(a, b); println!("{} minus {} is {}", a, b, result); }
Rust中也有關鍵字for,它類似於其他語言中使用的for循環,但它們的實作完全不同。 Rust的for循環基本上是一種更強大的重複構造(迭代器)的語法糖。簡單地說,Rust中的for迴圈只適用於可以轉換為迭代器的類型。一種這樣的類型是Range類型。 Range類型可以指一系列數字。
fn main() { print!("Normal range: "); for i in 0..10 { print!("{},", i); } println!(); print!("Inclusive range: "); for i in 0..=10 { print!("(),", i); } }
1.3.9 自訂資料類型
自訂類型,是由使用者定義的類型。自訂類型可以由幾種類型組成。它們可以是基元類型的包裝器,也可以是多個自訂類型的組合。它們有3種形式:結構體、枚舉及聯合,或稱為struct、enum及union。它們允許你更輕鬆地表示自己的數據。自訂類型的命名規則遵循駝峰命名法(CamelCase)。
結構體
在Rust中,結構體聲明形式有3種。其中最簡單的是單元結構體(unit struct),它使用關鍵字struct進行聲明,接著是其名稱,並用分號作為結尾。
struct Dummy; fn main() { let value = Dummy; }
結構體的第2種形式是元組結構體(tuple struct),它具有關聯資料。其中的每個欄位都沒有命名,而是根據它們在定義中的位置進行引用。
struct Color(u8, u8, u8); fn main() { let white = Color(255, 255, 255); let red = white.0; let green = white.1; let blue = white.2; println!("Red value: {}", red); let orange = Color(255, 165, 0); let Color(r, g, b) = orange; println!("R: {}, G: {}, B: {} (orange)", r, g, b); let Color(r, _, b) = orange; }
對於5個以下的屬性進行資料建模時,元組結構是理想的選擇。除此之外的任何選擇都會妨礙程式碼的可讀性和推理。對於具有3個以上的字段的資料類型,建立使用類C語言的結構體,這是第3種形式,也是最常用的形式。
struct Player { name: String, iq: u8, friends: u8, score: u16 } fn bump_player_score(mut player: Player, score: u16) { player.score = score; println!("Updated player stats:"); } fn main() { let name = "Alice".to_string(); let player = Player { name, iq: 171, friends: 134, score: 1129 }; bump_player_score(player, 120); }
列舉
當你需要為不同類型的東西建模時,枚舉是一種好方法。它使用關鍵字enum創建,之後跟著的是枚舉名稱和一對花括號。在花括號內部,我們可以編寫所有可能的類型,即變體。這些變體可以在包含或不包含資料的情況下定義,並且包含的資料可以是任何甚遠類型、結構體、元組結構體,甚至是枚舉類型。
enum Direction { N, E, S, W } enum PlayerAction { Move { direction: Direction, speed: u8 }, Wait, Attack(Direction) } fn main() { let simulated_player_action = PlayerAction::Move { direction: Direction::N, speed: 2, }; match simulated_player_action { PlayerAction::Wait => println!("Player wants to wait"), PlayerAction::Move { direction, speed } => { println!("Player wants to move in direction {:?} with speed {}", direction, speed) } PlayerAction::Attack(direction) => { println!("Player wants to accack direction {:?}", direction) } }; }
1.3.10 類型上的函數和方法
結構體上的impl塊
我們可以使用兩種機制為定義的結構體添加行為:一種是類似建構函數的函數,另一種是設定getter和setter方法。
struct Player { name: String, iq: u8, friends: u8 } impl Player { fn with_name(name: &str) -> Player { Player { name: name.to_string(), iq: 100, friends: 100 } } fn get_friends(&self) -> u8 { self.friends } fn set friends(&mut self, count: u8) { self.friends = count; } } fn main() { let mut player = Player::with_name("Dave"); player.set_friends(23); println!("{}'s friends count: {}", player.name, player.get_friends()); let _ = Player::get_friends(&player); }
關聯方法:此方法沒有self型別為第1個參數。它類似於物件導向程式語言中的靜態方法。這些方法在類型本身上即可調用,並且不需要類型的實例來調用。透過在方法名稱前加上結構體名稱和雙冒號來呼叫關聯方法。
實例方法:將self作為第1外參數的函數。這裡的self類似於Python中的self,並且指向實作該方法的實例。
impl塊和枚舉
枚舉廣泛用於狀態機,當其與match表達式搭配使用時,可使狀態轉換程式碼非常簡潔。它們也可用於自訂錯誤類型的建模。
enum PaymentMode { Debit, Credit, Paypal } fn pay_by_credit(amt: u64) { println!("Processing credit payment of {}", amt); } fn pay_by_debit(amt: u64) { println!("Processing debit payment of {}", amt); } fn paypal_redirect(amt: u64) { println!("Redirecting to paypal for amount: {}", amt); } impl PaymentMode { fn pay(&self, amount: u64) { match self { PaymentMode::Debit => pay_by_debit(amount), PaymentMode::Credit => pay_by_credit(amount), PaymentMode::Paypal => paypal_redirect(amount) } } } fn get_saved_payment_mode() -> PaymentMode { PaymentMode::Debit } fn main() { let payment_mode = get_saved_payment_mode(); payment_mode.pay(512); }
1.3.11 module、import和use語句
程式語言通常會提供一種將大型程式碼區塊拆分為多個檔案以管理複雜性的方法。 Java遵循每個.java檔案就是公共類別的約定,而C 為我們提供了頭檔和include語句。 Rust提供了模組機制。模組是Rust程式中命名和組織程式碼的一種方式。
每個Rust程式都需要一個root模組。對於可執行文件,它通常是main.rs文件,對於程式庫,它通常是lib.rs文件。
模組可以在其他模組內部聲明,也可以組織為檔案和目錄。
為了讓編譯器能夠識別我們的模組,需要使用關鍵字mod聲明,在我們的root模組中,要在模組名稱前使用關鍵字use,這表示將元素引入作用域。
模組中定義的元素預設是私有的,需要使用關鍵字pub將它暴露給呼叫方。
1.3.12 集合
陣列
數組是有固定長度,可以儲存相同類型的元素,它們用[T, N]表示,其中T表示任意類型,N表示數組元素的數量。陣列的大小不能用變數表示,而且必須是usize的字面值(literal)。
元組
項目清單
鍵/值對
切片
1.3.13 迭代器
子主題
子主題
子主題
1.4 改進字元計數器
1.5 小結
第2章 使用Cargo管理項目
2.1 軟體包管理器
2.2 模組
2.2.1 嵌套模組
2.2.2 將檔案用作模組
2.2.3 將目錄用作模組
2.3 Cargo和程式庫
2.3.1 新建一個Cargo項目
2.3.2 Cargo與依賴項
2.3.3 使用Cargo執行測試
2.3.4 使用Cargo運行範例
2.3.5 Cargo工作區
2.4 Cargo工具擴展
2.4.1 子命令和Cargo安裝
2.4.2 使用clippy格式化程式碼
2.4.3 Cargo.toml清單文件簡介
2.5 建置Rust開發環境
2.6 使用Cargo建構imgtool程序
2.7 小結
第3章 測試、文檔化和基準評估
3.1 測試的目的
3.2 組織測試
3.3 單元測試
3.3.1 第一個單元測試
3.3.2 運行測試
3.3.3 隔離測試程式碼
3.3.4 故障測試
3..3.5 忽略測試
3.4 集成測試
3.4.1 第一個整合測試
3.4.2 共享通用代碼
3.5 文件
3.5.1 編寫文檔
3.5.2 產生和檢視文檔
3.5.3 託管文檔
3.5.4 文檔屬性
3.5.5 文檔化測試
3.6 基準
3.6.1 內建的微觀基準工具
3.6.2 穩定版Rust上的基準測試
3.7 編寫和測試軟體包-邏輯閘模擬器
3.8 CI整合測試與Travis CI
3.9 小結
第4章 類型、泛型和特徵
4.1 類型系統及其重要性
4.2 泛型
4.2.1 建立泛型#
4.2.2 泛型實現
4.2.3 泛型應用
4.3 用特徵抽象行為
4.3.1 特徵
4.3.2 特徵的多種形式
4.4 使用包泛型的特徵-特徵區間
4.4.1 類型上的特徵區間
4.4.2 泛型函數和impl程式碼區塊上的特徵區間
4.4.3 使用「 」特徵組合為區間
4.4.4 特徵區間與impl特徵語法
4.5 標準庫特徵簡介
4.6 使用特徵物件實現真正的多態性
4.6.1 分發
4.6.2 特徵對象
4.7 小結
第5章 記憶體管理與安全性
5.1 程序和內存
5.2 程式如何使用內存
5.3 記憶體管理及其分類
5.4 記憶體分配簡介
5.4.1 堆疊
5.4.2 堆
5.5 記憶體管理的缺陷
5.6 記憶體安全性
5.7 記憶體安全三原則
5.7.1 所有權
5.7.2 透過特徵復用類型
5.7.3 借用
5.7.4 基於借用規則的方法類型
5.7.5 生命週期
5.8 Rust中的指標類型
5.8.1 引用-安全的指針
5.8.2 原始指針
5.8.3 智慧指針
5.8.4 引用計數的智慧指針
5.8.5 內部可變性的應用
5.9 小結
第6章 異常處理
6.1 異常處理簡介
6.2 可恢復的異常
6.2.1 Option
6.2.2 Result
6.3 Option/Result的組合
6.3.1 常見的組合器
6.3.2 組合器應用
6.3.3 Option和Result類型之間的轉換
6.4 及早返回和運算子“?”
6.5 不可恢復的異常
6.6 自訂錯誤和Error特徵
6.7 小結
第7章 高級概念
7.1 類型系統簡介
7.1.1 程式碼區塊和表達式
7.1.2 let語句
7.1.3 循環作為表達式
7.1.4 數字類型中的類型清晰度和符號區分
7.1.5 類型推斷
7.1.6 類型別名
7.2 字串
7.2.1 包含所有權的字串——String
7.2.2 借用字串——&str
7.2.3 字串切片和分塊
7.2.4 在函數中使用字串
7.2.5 字串拼接
7.2.6 &str和String的應用場景
7.3 全域值
7.3.1 常量
7.3.2 靜態值
7.3.3 編譯期函數-const fn
7.3.4 透過lazy_static宏將靜態值動態化
7.4 迭代器
7.5 高級類型
7.5.1 不定長類型
7.5.2 函數類型
7.5.3 never類型“!”和函數分發
7.5.4 聯合
7.5.5 Cow
7.6 高級特徵
7.6.1 Sized和?Sized
7.6.2 Borrow和AsRef
7.6.3 ToBoned
7.6.4 From和Into
7.6.5 特徵對象和物件安全性
7.6.6 通用函數呼叫語法
7.6.7 特徵規則
7.7 閉包進階
7.7.1 Fn閉包
7.7.2 FnMut閉包
7.7.3 FnOnce閉包
7.8 結構體、枚舉和特徵中的常數
7.9 模組、路徑和導入
7.9.1 導入
7.9.2 再次導入
7.9.3 隱私性
7.10 高級匹配模式和守護
7.10.1 匹配守護
7.10.2 高階let構建
7.11 強制型別轉換
7.12 型別與內存
7.12.1 內存對齊
7.12.2 std::mem模組
7.13 使用serde進行序列化和反序列化
7.14 小結
第8章 並發
8.1 程式執行模型
8.2 並發
8.2.1 並發方法
8.2.2 缺陷
8.3 Rust中的並發
8.3.1 線程基礎
8.3.2 自訂線程
8.3.3 存取線程中的數據
8.4 線程的並發模型
8.4.1 狀態共享模型
8.4.2 互斥
8.4.3 透過Arc和Mutex實現共享可變性
8.4.4 透過訊息傳遞進行通信
8.5 Rust中的線程安全
8.5.1 什麼是線程安全
8.5.2 線程安全的特徵
8.5.3 Send
8.5.4 Sync
8.6 使用actor模型實現並發
8.7 其他程式庫
8.8 小結
第9章 宏與元編程
9.1 什麼是元程式設計?
9.2 Rust宏的應用場景
9.3 Rust中的巨集及其型別
9.4 使用macro_rules!建立宏
9.5 標準庫中的內建宏
9.6 macro_rules!巨集的標記類型
9.7 巨集中的重複
9.8 巨集的高階應用-為HashMap的初始化編寫DSL
9.9 巨集用例-編寫測試
9.10 練習
9.11 過程宏
9.12 派生宏
9.13 高度宏程序
9.14 常用的過程巨集軟體包
9.15 小結
第10章 不安全的Rust和外部函數接口
10.1 安全與不安全
10.1.1 不安全的函數和程式碼區塊
10.1.2 不安全的特徵和實現
10.2 在Rust中呼叫C程式碼
10.3 透過C語言呼叫Rust程式碼
10.4 在Rust使用外部C/C 程式庫
10.5 使用PyO3建構原生Python擴展
10.6 在Rust中為Node.js建立原生擴展
10.7 小結
第11章 日誌
11.1 日誌記錄及其重要性
11.2 日誌記錄框架的需求
11.3 日誌記錄框架及其特性
11.4 日誌記錄方法
11.4.1 非結構化日誌記錄
11.4.2 結構化日誌記錄
11.5 Rust中的日誌記錄
11.5.1 log——為Rust日誌記錄
11.5.2 env_logger
11.5.3 log4rs
11.5.4 使用slog進行結構化日誌記錄
11.6 小結
第12章 Rust與網路編程
12.1 網路程式設計簡介
12.2 同步網路I/O
12.3 非同步網路I/O
12.3.1 Rust中的非同步抽象
12.3.2 建構異步的Redis伺服器
12.4 小結
第13章 用Rust建構Web應用程式
13.1 Rust中的Web應用
13.2 用hyper進行HTTP通信
13.2.1 hyper伺服器端API建置一個短網址服務
13.2.2 作為客戶端的hyper-建構一個URL短網址客戶端
13.2.3 Web框架
13.3 actix-web基礎知識
13.4 使用actix-web建立一個書籤API
13.5 小結
第14章 Rust與WebAssembly
14.1 資料持久性的重要性
14.2 SQLite
14.3 PostgreSQL
14.4 r2d2連接池
14.5 Postgres和diesel ORM
14.6 小結
第15章 Rust和WebAssembly
15.1 什麼是WebAssmbly
15.2 WebAssembly的設計目標
15.3 WebAssembly入門
15.3.1 線上嘗試
15.3.2 產生WebAssembly的方法
15.4 Rust和WebAssembly
15.4.1 wasm-bindgen
15.4.2 其他WebAssembly項目
15.5 小結
第16章 Rust與桌面應用
16.1 GUI開發簡介
16.2 GTK 框架
16.3 透過gtk-rs建立一個新聞類桌面應用程式
16.4 練習
16.5 其他新興的UI框架
16.6 小結
第17章 調試
17.1 調試簡介
17.1.1 調試器基礎
17.1.2 調試的先決條件
17.1.3 配置GDB
17.1.4 一個範例程式——buggie
17.1.5 GDB基礎知識
17.1.6 在Visual Studio Code中整合GDB
17.2 rr調試器簡介
17.3 小結