The Rust Programming Language (第1版)
好的,這是針對您提供的 Rust 程式語言資料所提取的主要論點及詳盡解釋:
Rust 程式語言的核心理念與特性
您提供的資料首先闡述了 Rust 作為一個系統程式語言的核心目標:安全性 (safety)、速度 (speed) 和併行性 (concurrency)。它強調 Rust 在沒有垃圾收集器 (garbage collector) 的情況下達成了這些目標,這使得 Rust 非常適合其他語言難以處理的應用場景,例如:
- 嵌入到其他語言中 (embedding in other languages):由於沒有 GC,Rust 的執行時非常小,便於與其他語言的運行時共存。
- 具有特定空間和時間要求的程式 (programs with specific space and time requirements):系統程式通常需要精確控制記憶體佈局和執行時間,Rust 提供了這樣的低層級控制能力。
- 編寫低層級程式碼 (writing low-level code):例如裝置驅動程式和作業系統,這些需要直接與硬體互動的程式碼,Rust 能夠提供必要的控制和安全性。
資料指出,Rust 透過一系列編譯時的安全檢查 (compile-time safety checks) 來改進現有的系統程式語言,這些檢查不會產生額外的執行時開銷 (no runtime overhead)。特別強調的是,Rust 消除了所有資料競爭 (data races)。同時,Rust 旨在實現「零成本抽象」(zero-cost abstractions),即使某些抽象感覺像高階語言的特性,Rust 仍然允許像低層級語言一樣進行精確控制 (precise control)。
這段介紹為後續章節奠定了基礎,表明 Rust 在追求效能的同時,將安全性放在了極為重要的位置,並通過編譯時檢查而非執行時的 GC 或昂貴檢查來實現這一點。
入門與基本工具
資料的第二部分詳細介紹了如何開始使用 Rust,包括安裝、編寫第一個程式以及使用專案管理工具 Cargo。
-
安裝 Rust (Installing Rust):推薦使用
rustup工具進行安裝,它簡化了在不同平台(如 Unix 系統和 Windows)上的安裝過程。同時也提供了卸載和故障排除的說明,例如檢查rustc --version以及確保 Cargo 的二進位路徑在 PATH 環境變數中。 -
「Hello, world!」程式:這是學習新語言的傳統開端。
- 介紹了 Rust 原始檔的命名規則(
.rs副檔名,多單字用下劃線)。 - 展示了基本的程式結構:
fn main() { ... }定義了程式的入口函式main。 - 引入了
println!()巨集,用於在螢幕上印出文字。注意到println!是一個巨集 (macro),而不是普通函式,這通過名稱後的!區分。 - 解釋了字串文字 (string literal)
"Hello, world!"是靜態分配的。 - 指出了大多數 Rust 程式碼行以分號
;結尾,標記表達式的結束。 - 展示了編譯和執行是分離的兩個步驟:
rustc main.rs產生可執行檔,然後./main(或.\main.exe在 Windows 上) 執行。這與動態語言的單一步驟不同,強調 Rust 是「預先編譯」(ahead-of-time compiled) 的語言,編譯後的可執行檔無需 Rust 環境即可運行。
- 介紹了 Rust 原始檔的命名規則(
-
Hello, Cargo!:引入 Cargo 作為 Rust 的建置系統 (build system) 和套件管理器 (package manager)。
- Cargo 管理三件事:建置程式碼、下載依賴函式庫、建置依賴函式庫。
- 將先前的「Hello, world!」專案轉換為 Cargo 專案的步驟:建立
src目錄並移動原始檔至src/main.rs,刪除舊的可執行檔,建立Cargo.toml設定檔。 -
Cargo.toml檔案使用 TOML 格式,[package]區塊包含專案基本資訊(名稱、版本、作者)。 - 使用
cargo build編譯專案,可執行檔會產生在target/debug目錄下。 - 使用
cargo run可以一步完成編譯和執行。Cargo 會檢查原始檔是否修改,只在必要時重新建置。 - 介紹了
cargo build --release用於編譯帶有優化的釋出版本,可執行檔在target/release下。 - 解釋了
Cargo.lock檔案的作用,它記錄了依賴項的確切版本,確保可重複建置。 - 介紹了
cargo new <project_name> --bin命令,可以快速建立一個新的、帶有 Cargo 結構的二進位專案。
這部分強調了 Cargo 在 Rust 開發中的核心地位,它是管理複雜專案、依賴關係和標準建置流程的關鍵工具。
猜數字遊戲教學
這個教學專案實踐性地引入了 Rust 的一些基本語法和概念,這些概念隨後在「語法和語意」章節中會更詳細地解釋。
-
專案設置:使用
cargo new guessing_game --bin建立新專案,並確認Cargo.toml和src/main.rs結構。 -
處理猜測:
- 引入
std::io模組用於輸入/輸出。 - 使用
println!印出提示訊息。 - 聲明可變變數
let mut guess = String::new();,String::new()建立一個新的、空的可變字串。 - 使用
io::stdin().read_line(&mut guess)讀取使用者輸入,&mut guess表示將guess的可變引用傳遞給read_line函式。 - 引入
Result類型和.expect()方法:read_line返回io::Result,.expect("...")是一個便捷方法,如果Result是錯誤 (Err),則會觸發 panic 並印出訊息。如果成功 (Ok),則返回成功值。 - 印出讀取到的猜測結果。
- 引入
-
產生秘密數字:
- 引入外部 crate
rand。需要在Cargo.toml的[dependencies]區塊中添加rand = "0.3.0"(或其他版本)。 - 使用
extern crate rand;在程式碼中聲明使用外部 crate。 - 使用
use rand::Rng;引入rand::Rng特徵,以便使用隨機數生成器的方法。 - 使用
rand::thread_rng().gen_range(1, 101)產生一個介於 1 到 100 之間的隨機整數(範圍是左閉右開)。 - 將生成的秘密數字綁定到不可變變數
secret_number。
- 引入外部 crate
-
比較猜測:
- 引入
std::cmp::Ordering列舉,它有Less,Greater,Equal三個變體,表示比較的結果。 - 使用
guess.cmp(&secret_number)比較使用者猜測和秘密數字。cmp方法返回Ordering列舉值。注意到這裡需要處理類型不匹配,使用者輸入是字串,秘密數字是數字。 - 使用
match表達式根據cmp的結果進行分支處理。這是 Rust 處理列舉和模式匹配的常用方式。 -
處理類型轉換錯誤:使用者輸入的
guess是String,秘密數字是數字(預設i32)。直接比較會導致編譯錯誤(類型不匹配)。需要將字串轉換為數字。 - 使用
guess.trim().parse()嘗試將字串轉換為數字。trim()移除字串首尾空白,parse()嘗試將字串解析為指定數字類型。 -
parse()返回Result<NumberType, ParseError>。使用.expect("...")在解析失敗時 panic。 - 使用變數遮蔽 (shadowing)
let guess: u32 = guess.trim().parse().expect("...");將舊的String類型的guess遮蔽為新的u32類型的guess。這裡指定了目標類型為u32(無符號 32 位整數)。
- 引入
-
迴圈:
- 引入
loop關鍵字創建無限迴圈,讓使用者可以多次猜測。 - 在
match表達式中,當猜測正確時,使用break;跳出迴圈,結束程式。 -
處理非數字輸入:當
parse()失敗(使用者輸入非數字)時,之前的.expect()會 panic。修改為使用match來處理parse()返回的Result。 - 使用
match guess.trim().parse() { Ok(num) => num, Err(_) => continue, };來處理解析結果。如果成功 (Ok(num)),則將解析出的數字賦值給guess。如果失敗 (Err(_)),則使用continue;跳過本次迴圈迭代,要求使用者重新輸入。_模式用於匹配所有錯誤變體而不關心其具體值。
- 引入
- 完成:移除印出秘密數字的行,遊戲完成。
教學部分通過一個實際的範例,讓使用者接觸了 Rust 的核心語法元素(變數、類型、函式、控制流、模組、外部 crate、錯誤處理)及其組合使用。
語法和語意(部分)
資料的第四部分開始深入探討 Rust 的語法和語意細節。
-
變數綁定 (Variable Bindings):
- 使用
let關鍵字創建綁定,例如let x = 5;。 -
let的左側是一個模式 (pattern),不僅僅是變數名,可以用於解構,例如let (x, y) = (1, 2);。 - Rust 是一個靜態類型語言 (statically typed language),但具有類型推斷 (type inference) 能力,通常無需顯式指定類型。可以通過
: type進行類型註解,例如let x: i32 = 5;。 - 變數綁定預設是不可變的 (immutable by default)。使用
mut關鍵字使其可變,例如let mut x = 5;。這是一種安全設計,幫助防止意外變更。 - 變數綁定在使用前必須初始化 (required to be initialized)。使用未初始化變數會導致編譯錯誤。
- 變數綁定具有作用域 (scope),限定在定義它們的程式碼塊
{}中。 - 可以遮蔽 (shadowing) 先前的變數綁定,即在相同作用域或內部作用域使用
let關鍵字重新定義同名變數。遮蔽允許改變變數的類型或可變性。
- 使用
-
函式 (Functions):
- 使用
fn關鍵字定義函式,main函式是入口點。 - 函式參數必須顯式註明類型,例如
fn print_number(x: i32)。 - 使用
-> type語法指定返回值類型,例如fn add_one(x: i32) -> i32。 - Rust 是表達式導向的語言 (expression-oriented language)。大多數事物都是表達式,它們會返回一個值。陳述式 (statement) 不返回值。
- Rust 有兩種陳述式:聲明陳述式 (Declaration Statement, 如
let) 和表達式陳述式 (Expression Statement)。 - 大多數程式碼行以分號
;結尾,將表達式轉換為陳述式。但函式的最後一個表達式通常不加分號,它的值會作為函式的返回值。 - 可以使用
return關鍵字實現早期返回,但作為函式最後一行時通常被認為風格不佳。 -
發散函式 (Diverging functions):使用
!作為返回類型,表示函式永不返回(例如panic!())。發散函式可以用於任何需要返回值的語境。 - 可以將函式綁定到變數,形成函式指標 (function pointers),其類型語法為
fn(ArgType) -> ReturnType。
- 使用
-
基本類型 (Primitive Types):
-
布林 (Booleans):
bool類型,值為true或false。 -
字元 (char):代表單一的 Unicode Scalar Value,使用單引號
'a'。Rust 的char佔 4 個位元組。 -
數字類型 (Numeric types):
- 整數:帶符號 (
i) 和無符號 (u),固定大小 (8, 16, 32, 64 位),例如i32,u64。 - 變動大小類型:
isize,usize,大小取決於機器架構,用於表示大小或索引。 - 浮點數:
f32(單精度),f64(雙精度)。 - 數字文字如果沒有明確註解或推斷,預設為
i32(整數) 或f64(浮點數)。
- 整數:帶符號 (
-
陣列 (Arrays):
[T; N],固定大小的同類型元素列表,大小N是編譯時常數。預設不可變。通過a.len()獲取長度。通過a[index]索引存取,索引從 0 開始,執行時檢查越界。 -
切片 (Slices):
&[T]或&mut [T],對其他資料結構(如陣列或向量)的「引用」或「視圖」,提供安全、高效的部分存取,無複製。內部表示為指向數據開頭的指標和長度。使用&data[start..end]語法創建。 -
str:最基礎的字串類型,是非大小類型 (unsized type),通常只通過引用
&str使用。
-
布林 (Booleans):
-
元組 (Tuples):
-
(...),固定大小的有序列表,元素可以是不同類型(異質)。例如(1, "hello")。 - 類型表示為
(type1, type2, ...)。 - 可以使用模式解構
let (x, y) = tuple;來存取元素。 - 可以使用索引
.0,.1等來存取元素。 - 單元素元組需要加逗號區分,例如
(0,)。
-
-
註解 (Comments):
- 行註解:
// ...到行尾。 - 文件註解 (Doc comments):
/// ...用於文檔化緊隨其後的項目,//! ...用於文檔化包含它的項目(如模組)。支持 Markdown 語法,rustdoc工具用於生成 HTML 文檔並可以測試程式碼範例。
- 行註解:
-
if 控制流:
- 標準的
if condition { ... } else { ... } else if condition { ... }結構。 -
if是一個表達式,可以返回一個值。分支中的最後一個表達式的值就是if表達式的值。
- 標準的
-
迴圈 (Loops):
-
loop { ... }:無限迴圈,直到遇到break或return。 -
while condition { ... }:條件迴圈,當條件為真時執行。 -
for var in expression { ... }:迭代器迴圈,遍歷表達式(實現IntoIterator特徵)產生的迭代器中的每個元素。範圍0..10是一個常見的迭代器。 -
enumerate():迭代器適配器,產生 (索引, 值) 對。 -
break:立即終止最內層迴圈。 -
continue:跳過當前迭代的剩餘部分,繼續下一輪迭代。 -
迴圈標籤 (Loop labels):使用
'label:語法為迴圈命名,break 'label;或continue 'label;可以指定跳出或繼續哪一層外部迴圈。
-
-
向量 (Vectors):
-
Vec<T>,可增長的 (growable) 同類型元素陣列,數據儲存在堆 (heap) 上。 - 使用
vec! macro創建,例如vec![1, 2, 3]或vec。 - 索引存取與陣列類似 (
v[index]),索引類型為usize,執行時檢查越界,越界會 panic。 -
.get(index)方法用於安全存取,返回Option<&T>,越界時返回None。 - 可以通過
&v,&mut v,v三種方式遍歷向量,分別得到不可變引用、可變引用或轉移所有權的迭代器。
-
-
所有權 (Ownership):
- Rust 的核心概念,用於保證記憶體安全 (memory safety) 而無需 GC。
- 每個值都有一個所有者 (owner)。
- 一次只能有一個所有者。
- 當所有者超出作用域時,值會被丟棄 (drop),釋放資源。
-
移動語意 (Move semantics):非
Copy類型的賦值或作為函式參數傳遞會將所有權從原綁定移動到新綁定或函式參數。移動後,原綁定失效,不能再使用。這是為了防止 double free。 -
複製語意 (Copy semantics):實現
Copy特徵的類型(通常是固定大小、不包含任何指針或資源句柄的類型,如基本數字類型)在賦值或傳遞時會進行位元組複製,而原綁定仍然有效。 - 所有權轉移是預設行為,沒有額外開銷。
-
引用與借用 (References and Borrowing):
- 借用 (borrowing) 允許在不轉移所有權的情況下使用一個值。
-
引用 (references) 是借用的機制,使用
&創建。 -
&T是不可變引用 (immutable reference),允許多個讀者。 -
&mut T是可變引用 (mutable reference),允許一個寫者。 -
借用規則 (The Rules):在任何給定時間,一個資源只能擁有:一個或多個不可變引用 (
&T),或者恰好一個可變引用 (&mut T)。這兩者不能同時存在。 - 這些規則由借用檢查器 (borrow checker) 在編譯時 (at compile time) 強制執行,無執行時開銷。
- 借用的生命週期不能長於所有者的作用域。
- 借用規則有效防止了資料競爭、迭代器失效、use-after-free 等問題。
-
生命週期 (Lifetimes):
- 生命週期是編譯器用來確保引用有效的機制,與作用域緊密相關。
- 生命週期參數使用單引號
'a表示。 - 在函式或結構體簽名中顯式標記生命週期,以告訴編譯器引用之間的關係(例如,一個引用不能比它指向的數據活得長)。
- 生命週期省略 (Lifetime Elision):在許多常見情況下,編譯器可以推斷生命週期,無需顯式標記。
-
'static生命周期:表示引用數據的生命週期與整個程式的執行時間一樣長(例如字串文字、static變數)。 - HRTB (Higher-Ranked Trait Bounds,
for<'a> Trait<'a>):用於更複雜的引用生命週期約束。
-
可變性 (Mutability):
-
let mut使綁定可變,可以重新綁定到不同的值。 -
&mut T可變引用允許通過引用修改數據。 -
外部可變性 vs. 內部可變性 (Exterior vs. Interior Mutability):預設的可變性是外部的(由綁定或引用控制)。
std::cell模組中的類型(如Cell<T>,RefCell<T>)提供內部可變性,允許在不可變引用下修改內部數據,但會在執行時進行借用檢查。 - 欄位可變性:可變性是綁定的屬性,不是結構體本身的屬性。要修改結構體欄位,需要
let mut struct_instance = ...;或使用內部可變性類型。
-
-
結構體 (Structs):
-
struct Name { field1: type1, field2: type2, ... },定義自訂的複合數據類型。 - 通過
Name { field1: value1, ... }語法實例化,欄位順序無關。 - 使用點號
.存取欄位,例如instance.field1。 - 結構體欄位預設不可變,除非實例綁定是
mut。 - 更新語法
Struct { field: value, .. other_struct }:從另一個結構體複製未指定的欄位值。 -
元組結構體 (Tuple Structs):
struct Name(type1, type2, ...);,有名字但欄位沒有名字,像命名過的元組。用於 newtype 模式。 -
單元結構體 (Unit-like Structs):
struct Name;,沒有任何數據,像命名過的單元()。用於標記或實現特徵。
-
-
列舉 (Enums):
-
enum Name { Variant1, Variant2(Type1, ...), Variant3 { field: Type, ... }, ... },定義具有多個可能變體 (variant) 的類型。 - 每個變體可以有關聯的數據(無數據、元組形式、結構體形式)。
- 使用
Enum::Variant語法引用變體。 - 列舉是標記聯合 (tagged union):值包含一個標記指示它是哪個變體,以及該變體的數據。編譯器強制安全存取。
- 主要通過
match表達式進行模式匹配來處理列舉值。
-
-
Match 表達式:
-
match expression { pattern1 => result1, pattern2 => result2, ... }。 - 根據表達式的值與哪個模式匹配來執行相應的程式碼塊。
- 模式匹配功能強大,可以匹配字面值、變數、解構結構體/元組/列舉等。
-
窮舉性 (Exhaustiveness):
match必須覆蓋所有可能的模式。_通配符模式用於匹配所有未明確列出的情況,保證窮舉性。 -
match是一個表達式,其值是匹配到的分支的結果。
-
-
模式 (Patterns):
- 模式用於
let綁定、match表達式、if let/while let等語境中。 - 可以匹配字面值、變數名(會創建新綁定,可能遮蔽外部同名變數)、
_(忽略值)。 -
多重模式 (Multiple patterns):使用
|匹配多個模式。 -
解構 (Destructuring):匹配結構體、元組、列舉變體,並綁定其內部的值。可以使用
..忽略部分欄位。 -
ref/ref mut:在模式中創建對匹配到的值的引用。 - 範圍模式
...:匹配一個值的範圍。 - 綁定模式
@:variable @ pattern,將匹配到的模式的整體值綁定到variable。 -
匹配守衛 (Match guards):
pattern if condition => ...,在模式匹配成功後,再檢查附加的布林條件。
- 模式用於
-
方法語法 (Method Syntax):
- 使用
impl區塊為結構體或列舉定義方法。 - 方法是關聯函式,但第一個參數是特殊的接收者 (receiver):
self(值本身,轉移所有權)、&self(不可變引用,借用)、&mut self(可變引用,可變借用)。 - 方法通過點號
.呼叫,例如instance.method(...)。 -
關聯函式 (Associated functions):在
impl區塊中定義,但不接收self參數。用於構造函數或其他與類型相關但不依賴具體實例的功能。通過Type::function(...)呼叫。 - 可以鏈式呼叫 (chaining) 方法,如果方法返回
self或&self/&mut self。 - 建構者模式 (Builder Pattern):一種設計模式,通過一系列方法調用來逐步構建一個複雜對象,常與鏈式呼叫和關聯函式結合使用。
- 使用
-
字串 (Strings):
- Rust 的字串處理基於 UTF-8 編碼。
-
&str(字串切片):不可變、固定大小的 UTF-8 位元組序列引用。字串文字"..."的類型是&'static str。 -
String:可變、堆上分配、可增長的 UTF-8 位元組序列。通常從&str轉換而來,例如"".to_string()。 - 由於 UTF-8 是變長編碼,Rust 不支援直接通過整數索引存取字元 (
string[index]),因為這不是 O(1) 操作且不精確。可以通過.as_bytes()遍歷位元組,或通過.chars()遍歷 Unicode 字元。 - 切片語法 (
&string[start..end]) 是按位元組索引,必須切在有效的 UTF-8 字元邊界上,否則執行時會 panic。 - 連接:
String + &str,String轉移所有權。String + &String需要&。 -
Deref 強制轉型 (Deref coercions):如果類型
T實現了Deref<Target=U>特徵,那麼&T會自動強制轉型為&U。這使得&String可以自動轉型為&str。
-
泛型 (Generics):
- 允許編寫適用於多種類型的函式或數據結構,實現參數多型 (parametric polymorphism)。
- 使用
<T>或<T, E, ...>在函式、結構體、列舉定義中聲明類型參數。 - 類型參數在使用時被具體類型替換,例如
Option<i32>,Result<String, io::Error>。 - 可以在
impl區塊中為泛型類型實現方法。 -
特徵約束 (Trait Bounds):使用
: Trait限制泛型類型必須實現特定的特徵,例如fn process<T: Display>(item: T)。 - 使用
+組合多個特徵約束。 -
where子句:用於複雜的特徵約束,提高程式碼可讀性。 -
解決歧義 (Resolving ambiguities):當編譯器無法推斷泛型類型時,需要類型註解或使用「turbofish」語法
::<Type>顯式指定。
-
特徵 (Traits):
- 特徵定義了一組方法的契約 (contract),類型可以選擇實現這些契約。
- 使用
trait Name { ... }定義特徵,包含方法簽名。 - 使用
impl Trait for Type { ... }為特定類型實現特徵。 -
Self關鍵字在特徵方法簽名中代表實現該特徵的具體類型。 - 特徵約束是泛型特徵的重要應用,確保泛型類型具有特定行為。
-
實現特徵的規則 (Rules for implementing traits):孤兒規則 (Orphan rule) – 要實現特徵
T對類型Y,T或Y至少一個必須在當前 crate 中定義。這防止了不同 crate 為同一類型實現同一特徵造成的衝突。 - 可以定義預設方法 (Default methods),在實現特徵時可以選擇不覆蓋。
- 特徵可以繼承 (inheritance) 其他特徵 (
trait TraitB: TraitA { ... }),實現TraitB必須同時實現TraitA。 -
派生 (Deriving):
#[derive(Trait)]屬性,讓編譯器自動為某些標準特徵(如Debug,Clone,Copy,PartialEq,Eq等)生成實現。 -
Drop 特徵:一個標準特徵,實現後,當值超出作用域時會自動執行其
drop方法,用於資源清理。
-
if let / while let:
- 簡化了對
Option或Result等類型進行模式匹配的常見場景。 -
if let pattern = expression { ... } else { ... }:如果模式匹配成功則執行第一個塊,否則執行else塊。 -
while let pattern = expression { ... }:只要模式持續匹配成功,就重複執行程式碼塊。
- 簡化了對
-
特徵物件 (Trait Objects):
- 用於動態派發 (dynamic dispatch),即在執行時確定呼叫哪個具體方法實現。
-
&Trait或Box<Trait>形式,可以在執行時持有任何實現Trait的類型實例。 - 特徵物件包含一個數據指標 (data pointer) (指向實際數據) 和一個 vtable 指標 (vtable pointer) (指向該具體類型實現特徵方法的函式指標表)。
- 呼叫方法時,通過 vtable 查找並呼叫正確的函式。
- 與靜態派發 (通過泛型和特徵約束實現,編譯時確定,有單態化和程式碼膨脹開銷但通常更快) 相比,動態派發有執行時查找開銷但可以減少程式碼膨脹。
-
物件安全性 (Object Safety):只有某些特徵可以成為特徵物件。主要限制是特徵方法不能對
Self類型有特殊要求(如方法返回Self或接收Self值)。
-
閉包 (Closures):
-
|parameters| -> return_type { body }語法,是匿名函式,可以捕獲環境 (environment) 中的變數。 - 閉包捕獲變數的方式:預設按需捕獲(借用
&或可變借用&mut),或使用move關鍵字強制按值捕獲(移動或複製)。 -
move閉包將環境變數的所有權移入閉包,使其獨立於定義它的作用域。 - 閉包在內部被實現為實現了
Fn,FnMut,FnOnce三個特徵之一(或多個)的匿名結構體。這些特徵表示閉包是否可以被呼叫多次、是否會修改環境。 - 由於閉包是特徵,它們可以像其他特徵一樣作為函式參數(使用特徵約束或特徵物件)或返回值。
-
-
通用函式呼叫語法 (Universal Function Call Syntax, UFCS):
-
<Type as Trait>::function(receiver, ...)語法,用於明確指定呼叫特定類型Type實現的特徵Trait中的方法function。 - 當結構體本身和它實現的特徵中存在同名方法時,或者多個特徵實現了同名方法時,UFCS 用於消除歧義。
Trait::method(receiver)是其簡寫形式。
-
-
const 和 static:
-
const NAME: Type = value;:定義常數。值必須是常數表達式,編譯時計算並內聯到使用處,沒有固定的記憶體地址。必須註解類型。 -
static NAME: Type = value;:定義靜態變數(全域變數)。值必須是常數表達式,具有固定的記憶體地址且生命週期與程式相同。必須註解類型。 -
static mut NAME: Type = value;:定義可變靜態變數。讀取和寫入static mut都是不安全 (unsafe) 的操作,因為可能導致資料競爭。
-
-
屬性 (Attributes):
-
#[attribute](外層) 和#。外層屬性應用於緊隨其後的項目,內層屬性應用於包含它的項目。 - 用於向編譯器提供元數據,影響程式碼的編譯方式或行為。例如
#[test],#[cfg(feature = "..."),#[derive(...)],#[allow(...)],#[warn(...)],#[deny(...)],#[doc(...)]。 - 可以包含數據,例如
#[cfg(target_os = "macos")]。 -
#[cfg_attr(condition, attribute)]:根據條件應用另一個屬性。 -
cfg!巨集:在程式碼中檢查 cfg 旗標,返回布林值。
-
-
類型別名 (Type Aliases):
-
type AliasName = ExistingType;:為現有類型創建一個新的名字。 - 別名不是一個新的類型,只是原始類型的一個同義詞。別名與原始類型可以互相替換,不會引起類型不匹配錯誤。用於提高程式碼可讀性或簡化複雜類型。
-
-
類型轉換 (Casting Between Types):
-
as關鍵字:用於執行安全的類型轉換,主要用於數值類型轉換和指標轉換。數值轉換遵循特定規則(截斷、符號擴展等)。指標轉換允許在裸指標之間或裸指標與整數之間轉換。 -
transmute函式:std::mem::transmute::<SourceType, TargetType>(value)。執行不安全 (unsafe) 的任意類型轉換,直接將來源值的位元組解釋為目標類型。只檢查來源和目標類型的大小是否相同。使用transmute需要在unsafe塊內。
-
-
關聯類型 (Associated Types):
- 在特徵定義內部使用
type ItemName;聲明與特徵相關聯的類型成員。 - 允許在實現特徵時指定這些關聯類型的具體類型,例如
impl Iterator for Vec<T> { type Item = T; ... }。 - 解決泛型參數過多的問題,使特徵和泛型約束更簡潔。
- 在特徵物件中,如果特徵有關聯類型,則需要顯式指定關聯類型的具體類型,例如
Box<Iterator<Item = i32>>。
- 在特徵定義內部使用
-
非大小類型 (Unsized Types / DST):
- 指編譯時不知道具體大小的類型,如
str(字串切片指向的數據本身)、[T](切片指向的陣列數據)。 - DST 不能直接作為變數類型或函數參數類型,只能通過指標(如引用
&str,&[T],或Box<str>,Box<[T]>)來使用,因為指標本身是固定大小的。 - 結構體中只有最後一個欄位可以是 DST。
-
?Sized特徵約束:用於泛型參數,表示該泛型類型可以是非大小類型(預設泛型參數隱含Sized約束)。
- 指編譯時不知道具體大小的類型,如
-
運算子與重載 (Operators and Overloading):
- Rust 通過為類型實現
std::ops模組中的特定特徵來實現運算子重載,例如實現Add特徵來重載+運算子。 -
Deref特徵:用於重載解引用運算子*,是智能指標的基礎。 -
Deref 強制轉型:與
Deref特徵相關的語言特性,允許&T在需要&U的語境中自動轉型,如果T實現了Deref<Target=U>。這也適用於方法呼叫的接收者。
- Rust 通過為類型實現
-
裸指標 (Raw Pointers):
-
*const T(不可變) 和*mut T(可變)。 - Rust 中最低級別的指標,沒有所有權、生命週期、空值保證或自動清理。
-
解引用裸指標是不安全 (unsafe) 的操作,必須在
unsafe塊內進行。 - 主要用於與 C 語言的 FFI (Foreign Function Interface) 互動或編寫低層級安全抽象(如
Vec<T>)。 - 引用
&T可以安全地強制轉型為裸指標*const T。 - 從裸指標轉型為引用
&T是不安全的,需要unsafe塊,程式員需保證裸指標指向有效的數據且滿足引用的一切規則。
-
-
不安全 (Unsafe):
-
unsafe關鍵字標記的程式碼塊、函式、特徵或實現。 -
unsafe標記告訴編譯器,在這個範圍內,程式員承諾會手動維護記憶體安全等 Rust 的不變性,即使編譯器無法靜態驗證。 -
unsafe不關閉借用檢查或其他安全機制,它只允許程式員執行編譯器通常會禁止的五個操作:- 解引用裸指標。
- 呼叫不安全函式。
- 存取或修改
static mut變數。 - 實現不安全特徵。
- 訪問聯合 (union) 的欄位(未在提供的資料中,但屬於不安全操作)。
- 資料競爭、空/懸垂裸指標解引用、未初始化記憶體讀取、裸指標別名規則破壞等行為即使在
unsafe程式碼中仍然是未定義行為 (undefined behavior),必須避免。 - 所有 Rust 程式的崩潰源頭都可以追溯到
unsafe程式碼內部。
-
這份詳盡解釋覆蓋了您提供的資料中介紹的 Rust 語言的核心概念、工具鏈入門、實際專案範例以及基礎語法和語意細節,特別強調了 Rust 在記憶體安全、控制流、抽象機制和低層級控制之間的權衡與設計選擇。
comments
comments for this post are closed