Rust_Learning#
記錄我的 Rust 學習
Cargo 使用#
cargo new
cargo build
cargo run
cargo check
cargo doc --open #build documentation provided by all your dependencies locally and open it in your browser
參考鏈接#
Rust 特點#
- Patterns and the match construct
- a strong, static type system.
- type inference
- shadow
- Rust uses the term panicking when a program exits with an error
- requiring type annotations in function definitions
- Rust's goal is to compile programs into efficient binaries that require as few runtime checks as possible
- A foundational goal of Rust is to ensure that your programs never have undefined behavior
Chapter3 常見編程概念#
- constant v.s. variable
- 常量不僅默認為不可變;它們始終是不可變的。
- 常量可以在任何範圍內聲明
- 常量只能設置為常量表達式,而不能是只能在運行時計算的值的結果。
- shadow ends until
- 它本身被遮蔽
- 範圍結束
- 整數除法向零截斷到最近的整數
- tuple 和 array 區別
- tuple 的各類型可以不同;
- array 的大小固定,在定義時就指定了
- tuple 更靈活,可存儲不同類型,一般用於臨時組合數據
- array 大小固定,用於存儲大量相同類型的數據
- expression and statement
- 表達式不包括結束分號
- 語句不返回值
- 函數的返回值與函數主體塊中最後一個表達式的值同義
- 控制流
- 這意味著每個 if 的每個分支的潛在結果必須是相同類型
- Rust 沒有 “真” 或 “假” 值的概念。因此if 表達式的條件必須是布爾值
Chapter4 理解所有權#
- 堆棧保存與特定函數相關的數據,而堆保存可以超過函數生命週期的數據
- Rust 不允許程序手動釋放內存。這一政策避免了上述類型的未定義行為。
- Box 釋放原則:如果一個變量擁有一個 box,當 Rust 釋放變量的框架時,Rust 也釋放 box 的堆內存。
- 移動堆數據原則:如果變量 x 將堆數據的所有權移動到另一個變量 y,那麼 x 在移動後不能再使用。
- 引用是一種指針。
- Rust 在某些情況下隱式插入解引用和引用,例如使用點運算符調用方法
- 指針安全原則:數據不應同時被別名和修改。
- 權限是在路徑上定義的,而不僅僅是變量。路徑是您可以放在賦值左側的任何東西。
- 創建對數據的引用(“借用” 它)會使該數據在引用不再使用之前暫時變為只讀。
- Rust 的借用檢查器不包含 a [0]、a [1] 等不同路徑。它使用單一路徑 a [_] 來表示 a 的所有索引。
- 切片是一種特殊的引用,因為它們是 “胖” 指針,或帶有元數據的指針。在這裡,元數據是切片的長度。
總結#
- 優勢
- 通過避免垃圾回收來提高運行時性能
- 通過防止意外 “洩漏” 數據來提高可預測性。
- 指針可以通過
- boxes(擁有堆上數據的指針)
- references(非擁有指針)創建。
- move v.s. borrow
- 移動一個不可複製類型的變量(如 Box或 String)需要 RO 權限,並且移動會消除該變量的所有權限。這一規則防止了使用已移動的變量:
- 借用一個變量(創建對其的引用)會暫時移除該變量的一些權限
- 不可變借用創建一個不可變引用,並且禁用借用的數據被修改或移動。
- 可變借用創建一個可變引用,禁用借用的數據被讀取、寫入或移動
- use-after-free:不可變借用移除 W 權限以避免 use-after-free,
- double-frees:對不可複製數據的引用的解引用沒有 O 權限以避免 double-frees
Chapter5 結構體#
- Rust沒有構造函數的關鍵字。定義構造函數的慣用方法是創建一個名為 new 的關聯函數,但這並不是語言強制的。
- tuple 結構。例如
struct Color (i32,i32,i32);
- Rust 將插入所需的引用和解引用,以使 self 參數的類型匹配
- Rust 不會自動派生 Copy 以保持 API 變更的穩定性。
#[derive(Copy, Clone)]
- 當您看到類似 “cannot move out of *self” 的錯誤時,通常是因為您嘗試在引用上調用 self 方法,如 & self 或 & mut self。Rust 正在保護您免受雙重釋放
Chapter6 列舉#
-
使用枚舉而不是結構的優勢:
- 每個變體可以具有不同類型和數量的關聯數據
- 我們定義的每個枚舉變體的名稱也成為構造枚舉實例的函數
- 您可以在枚舉變體中放入任何類型的數據:字符串、數字類型或結構。例如,您甚至可以包含另一個枚舉
-
Option 枚舉
- 編譯器可以檢查您是否處理了所有應該處理的情況
- null 是一個當前無效或因某種原因缺失的值。
- Rust 沒有 null,但它有一個枚舉可以編碼值存在或缺失的概念。
- 函數 Option::unwrap 期望自我,這意味著它期望擁有arg。然而 arg 是一個不可變引用到一個選項,因此它無法提供選項的所有權。
-
match
-
每個 match 從上到下嘗試
-
opt 是一個普通的枚舉 —— 它的類型是 Option而不是像 & Option那樣的引用。因此對 opt 的匹配將移動未被忽略的字段,如 s。
-
如果我們想在不移動其內容的情況下查看 opt,慣用的解決方案是對引用進行匹配:
-
if let
- if let 作為語法糖,用於在值匹配一個模式時運行代碼,然後忽略所有其他值。
- 與 else 一起的代碼塊與與 if let 等效的 match 表達式中的_情況的代碼塊相同
Chapter7 使用包、crate 和模塊管理增長的項目#
包:一個 Cargo 功能,讓您構建、測試和共享 crate#
- 一個或多個 crate 的捆綁,提供一組功能。
- 一個包可以包含任意多的二進制 crate,但最多只能有一個庫 crate。
- 使用外部包
- 標準 std 庫也是一個外部於我們包的 crate。我們不需要更改 Cargo.toml 以包含 std。但我們需要使用 use 來引用它,以將那裡的項目帶入我們包的範圍
Crates:生成庫或可執行文件的模塊樹#
- 二進制 crate:必須有一個名為
main
的函數 - 庫 crate:定義旨在與多個項目共享的功能。
Rustaceans 說 “crate”,他們指的是庫 crate,並且他們將 “crate” 與一般編程概念 *“庫”* 互換使用。
模塊和使用:讓您控制路徑的組織、範圍和隱私#
- 用處
- 讓我們在 crate 內組織代碼以提高可讀性和易於重用
- 允許我們控制項目的隱私,因為模塊內的代碼默認是私有的
- 父和子
- 所有項目(函數、方法、結構、枚舉、模塊和常量)默認對父模塊私有。
- 父模塊中的項目無法使用子模塊內的私有項目,但子模塊中的項目可以使用其祖先模塊中的項目。
路徑:命名項目的方式,例如結構、函數或模塊#
- 慣用方法
- 使用 use 將函數的父模塊帶入範圍
- 當使用 use 引入結構、枚舉和其他項目時,慣用的做法是指定完整路徑
use std::io::Result as IoResult;
- 我們可以使用嵌套路徑在一行中將相同的項目帶入範圍。
use std::{cmp::Ordering, io};
,use std::io::{self, Write};
Chapter8#
向量#
- 對第一個元素的引用關心向量末尾的變化的原因
- 在向量末尾添加新元素可能需要分配新內存並將舊元素複製到新空間
- 對第一個元素的引用將指向已釋放的內存
- Vec::push 移動其參數,因此在調用 v.push (s) 後 s 不可用
- 當向量被丟棄時,它的所有內容也會被丟棄,這意味著它所持有的整數將被清理。
字符串#
- 編譯器可以將 & String 參數強制轉換為 & str
- Rust 字符串不支持索引以避免返回意外值並導致可能不會立即發現的錯誤
- 從 Rust 的角度看字符串的三種相關方式
- 字節
- 標量值
- 字形集
- 操作字符串片段的最佳方法是明確說明您想要字符還是字節
- &str 是一個承諾,指向的字節序列將始終是有效的 UTF-8
哈希映射#
- 當您想要通過使用鍵(可以是任何類型)而不是使用索引(如向量)來查找數據時,哈希映射非常有用
- 對於實現 Copy 特徵的類型,如 i32,值將被複製到哈希映射中。- 對於擁有的值如 String,值將被移動,哈希映射將成為這些值的擁有者
chapter10#
泛型數據類型#
- Rust 要求您提前聲明泛型類型的預期能力
- 如果沒有限制,泛型類型 T 沒有能力:它不能被打印、克隆或修改(儘管它可以被丟棄)。
- Rust 沒有類似於面向對象語言中專門化方法的繼承機制,
- 單型化是將泛型代碼轉換為具體代碼的過程,通過填充編譯時使用的具體類型
- const 泛型:
const N : usize
特徵#
- 特徵定義特定類型擁有的功能,並可以與其他類型共享。
- 一個需要注意的限制是,我們只能在類型上實現特徵,如果特徵或類型至少有一個是本地於我們的 crate。
- 默認實現可以調用同一特徵中的其他方法,即使這些其他方法沒有默認實現
- 參數中的特徵
fn some_function<T:Display + Clone ,U: Clone + Debug>(t:&T,u:&U) -> i32{} // 使用where子句清晰的特徵邊界 fn some_function<T,U>(t:&T,u:&U) -> i32 where T:Display + Clone , U: Clone + Debug {}
- 只能在返回單一類型時使用 impl Trait
- 使用特徵邊界有條件地實現方法
use std::fmt::Display; struct Pair<T> { x: T, y: T, } impl<T> Pair<T> { fn new(x: T, y: T) -> Self { Self { x, y } } } impl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest member is x = {}", self.x); } else { println!("The largest member is y = {}", self.y); } } }
- 有條件地為實現另一個特徵的任何類型實現特徵
impl<T: Display> ToString for T { // --snip-- }
生命週期#
- 生命週期註釋不會改變任何引用的生命週期。相反,它們描述多個引用之間的生命週期關係,而不影響生命週期
- 生命週期參數的名稱必須以撇號(')開頭,通常都是小寫且非常簡短
- 當我們在這個函數簽名中指定生命週期參數時,我們並沒有改變任何傳入或返回值的生命週期。相反,我們指定借用檢查器應拒絕任何不符合這些約束的值。
- 在 Rust 的早期版本(1.0 之前),每個引用都需要明確的生命週期
- 編譯器使用三條規則來確定引用的生命週期,當沒有明確的註釋時。
- 編譯器為每個輸入類型中的每個生命週期分配不同的生命週期參數。
- 如果恰好有一個輸入生命週期參數,則該生命週期分配給所有輸出生命週期參數
- 如果有多個輸入生命週期參數,但其中一個是
&self
或&mut self
,因為這是一個方法,則 self 的生命週期分配給所有輸出生命週期參數。
- 靜態生命週期
'static
- 字符串字面量直接存儲在程序的二進制中,始終可用。因此,所有字符串字面量的生命週期是
'static
。 - 'static 意味著 “在整個程序中存在”,因此靜態引用下的數據必須永遠不被釋放。
- 字符串字面量直接存儲在程序的二進制中,始終可用。因此,所有字符串字面量的生命週期是
- 生命週期標註並不會改變任何引用的實際作用域
chapter11#
自動生成測試的輸出#
- 0 測量統計數據是基準測試的性能測量。
Doc-tests
是任何文檔測試的結果
常用命令#
assert!();
assert_eq!();
assert_ne! // 如果我們給它的兩個值不相等,則將通過,並且如果它們相等,則將失敗。
並行或連續運行測試#
- 當您運行多個測試時,默認情況下它們使用線程並行運行
cargo test -- --test-threads=1
cargo test -- --show-output
cargo test --help
cargo test -- --help
-
#[test] #[ignore] cargo test -- --ignored cargo test -- --include-ignored
- 我們可以指定測試名稱的一部分,任何測試名稱與該值匹配的測試將被運行。
測試組織#
- 單元測試分別測試庫的不同部分,並可以測試私有實現細節。
- 單元測試分別測試庫的不同部分,並可以測試私有實現細節。
chapter12#
- 在所需函數嵌套在多個模塊中的情況下,我們選擇將父模塊帶入範圍而不是函數。
- TDD(測試驅動開發)
- 編寫一個失敗的測試並運行它,以確保它因您預期的原因而失敗。
- 編寫或修改足夠的代碼以使新測試通過。
- 重構您剛剛添加或更改的代碼,並確保測試繼續通過。
- 從第 1 步重複!
chapter13#
- Rust 將推斷閉包的參數 / 返回類型,但不會推斷頂層函數的類型
- 閉包可以以三種方式捕獲其環境中的值
- 不可變借用
- 可變借用
- 獲取所有權。
- Fn 特徵
- FnOnce
- FnMut
- Fn
- 迭代器適配器是懶惰的,我們需要在這裡消耗迭代器。