Phase 0:基礎語法快速通關
目標:掌握 Rust 的書寫規範,理解它如何處理最基本的資料。
1. 基礎型別(Data Types)
Rust 是靜態型別語言,每個變數在編譯期就必須知道它的型別。
大多數時候,Rust 可以自動推斷型別,但你也可以明確標註。
1.1 Scalar 純量型別
純量代表「單一個值」,共四種:
整數(Integer)
let a: i8 = 127; // 有號 8-bit: -128 ~ 127
let b: u8 = 255; // 無號 8-bit: 0 ~ 255
let c: i32 = -1_000_000; // 有號 32-bit(最常用,預設)
let d: u64 = 18_446_744; // 無號 64-bit
let e: isize = 100; // 平台相關大小(64-bit 系統 = i64)
let f: usize = 100; // 常用於陣列索引、長度
與 Java 的差異: Java 的
int固定是 32-bit,Rust 的i32也是,但 Rust 多了
u8~u128的無號型別,以及isize/usize這兩個平台相關型別。底線分隔符:
1_000_000和1000000完全相同,底線只是視覺分隔用。
浮點數(Float)
let x: f32 = 3.14; // 單精度(較少用)
let y: f64 = 3.14; // 雙精度(預設,精度更高)
// 注意:不能混合型別做運算!
// let z = x + y; // ❌ 編譯錯誤:mismatched types
let z = x as f64 + y; // ✅ 必須顯式轉型
布林(Boolean)
let t: bool = true;
let f: bool = false;
// Rust 不允許用整數代替布林,不像 C
// if 1 { } // ❌ 錯誤
字元(Char)
let c: char = 'A';
let emoji: char = '😀'; // ✅ Rust 的 char 是 4 bytes Unicode,可存任何 Unicode 字元
let chinese: char = '中'; // ✅ 完全合法
// 注意:char 用單引號 ' ',字串用雙引號 " "
與 C 的最大差異: C 的
char是 1 byte(ASCII),Rust 的char是 4 bytes(Unicode Scalar Value)。
1.2 Compound 複合型別
複合型別可以把多個值組合成一個型別。
元組(Tuple)— 固定長度、可混合型別
let tup: (i32, f64, char) = (42, 3.14, 'A');
// 解構(Destructuring)
let (x, y, z) = tup;
println!("x={}, y={}, z={}", x, y, z);
// 用索引存取
println!("第一個元素: {}", tup.0);
println!("第二個元素: {}", tup.1);
// 空元組 () 叫做 unit,代表「沒有值」(類似 void)
let nothing: () = ();
陣列(Array)— 固定長度、相同型別(存在 Stack 上)
let arr: [i32; 5] = [1, 2, 3, 4, 5]; // 型別標註:[元素型別; 長度]
let zeros = [0; 10]; // 建立 10 個 0 的陣列
println!("第一個: {}", arr[0]);
println!("長度: {}", arr.len());
// 越界存取會在 runtime panic,不會像 C 那樣造成 undefined behavior
// let invalid = arr[10]; // ❌ 會 panic: index out of bounds
Array vs Vec: Array 長度固定,存在 Stack;Vec 長度可變,存在 Heap。
大多數時候你會用
Vec<T>,但 Array 在效能敏感場合有優勢。
2. 函式(Functions)
2.1 基本語法
// 命名慣例:snake_case(不是 camelCase)
fn say_hello(name: &str, times: u32) {
for i in 0..times {
println!("Hello, {}! (第 {} 次)", name, i + 1);
}
}
// 有回傳值:用 -> 指定型別
fn add(x: i32, y: i32) -> i32 {
x + y // ⚠️ 注意:沒有分號!這是「表達式」,代表回傳這個值
}
2.2 語句(Statement)vs 表達式(Expression)— ⚠️ Rust 最重要的概念之一
這是 Rust 最讓新手困惑的地方,必須完全理解:
fn demonstrate() {
// 語句(Statement):執行動作,不回傳值,結尾有分號
let x = 5; // 這是語句
println!("{}", x); // 這也是語句(macro 呼叫)
// 表達式(Expression):計算並回傳一個值,沒有分號
let y = {
let inner = 3;
inner * 2 // ← 沒有分號 = 這個 block 的回傳值是 6
};
// y == 6
// 函式的隱含 return
// 以下兩個函式完全等價:
}
fn explicit_return(x: i32) -> i32 {
return x * 2; // 用 return 關鍵字(通常只在提早返回時用)
}
fn implicit_return(x: i32) -> i32 {
x * 2 // 最後一個表達式就是回傳值(慣用寫法)
}
// ⚠️ 常見錯誤:加了分號變成 ()
fn wrong_return(x: i32) -> i32 {
x * 2; // ❌ 這行加了分號變成「語句」,回傳 () 而不是 i32
// 編譯器會報錯:expected `i32`, found `()`
}
直覺理解: 想像分號是「我不在乎這個值,丟掉它」的符號。
不加分號 = 「把這個值往上傳」。
3. 控制流程(Control Flow)
3.1 if 表達式
let number = 7;
// 基本 if/else
if number > 5 {
println!("大於 5");
} else if number == 5 {
println!("等於 5");
} else {
println!("小於 5");
}
// ⚠️ 重點:if 在 Rust 中是「表達式」,可以回傳值!
let description = if number > 5 { "大" } else { "小" };
// 類似 Java/Python 的三元運算子,但更強大且清晰
// 注意:兩個分支的型別必須相同
// 可以用在 let 綁定:
let abs_value = if number >= 0 { number } else { -number };
3.2 loop — 無限迴圈
let mut counter = 0;
// loop 也是表達式,可以回傳值
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break 可以帶值,作為 loop 的回傳值
}
};
println!("result = {}", result); // 20
// 迴圈標籤(Nested loop 的 break/continue 控制)
'outer: loop {
'inner: loop {
break 'outer; // 直接跳出外層迴圈
}
}
3.3 while — 條件迴圈
let mut n = 3;
while n > 0 {
println!("{}!", n);
n -= 1;
}
println!("發射!");
3.4 for + Iterator — 最推薦的迴圈方式 ⭐
// 遍歷集合(最常用)
let fruits = ["apple", "banana", "cherry"];
for fruit in fruits {
println!("水果: {}", fruit);
}
// 數字範圍
for i in 0..5 { // 0, 1, 2, 3, 4(不包含 5)
print!("{} ", i);
}
for i in 0..=5 { // 0, 1, 2, 3, 4, 5(包含 5)
print!("{} ", i);
}
// enumerate() 同時取得索引和值
for (index, fruit) in fruits.iter().enumerate() {
println!("{}: {}", index, fruit);
}
// rev() 反向迭代
for i in (0..5).rev() {
println!("{}", i); // 4, 3, 2, 1, 0
}
為什麼推薦 for + Iterator?
不會有越界問題(不像
while i < arr.len())效能極佳(編譯器可以優化)
表達意圖更清晰
4. Macro 巨集初探
4.1 辨識 Macro
Macro 的呼叫以 ! 結尾,這是它們與普通函式的視覺區別:
println!("Hello, {}!", "world"); // macro
vec![1, 2, 3]; // macro
panic!("發生嚴重錯誤"); // macro
format!("數值是 {}", 42); // macro,回傳 String
assert!(1 + 1 == 2); // macro,測試用
assert_eq!(2 + 2, 4); // macro,比較兩個值
4.2 為什麼需要 Macro?
// println! 為什麼不是普通函式?
// 因為它接受不定數量、不定型別的參數:
println!("一個參數");
println!("兩個: {} {}", 1, "hello");
println!("三個: {} {} {}", 1, 2.0, 'c');
// 普通函式無法做到這件事(在沒有 macro 系統的語言中需要用 varargs 等機制)
// Macro 在「編譯期」展開成對應的程式碼,所以沒有執行期的負擔
4.3 格式化字串
let name = "Kirin";
let score = 95;
println!("姓名: {}", name); // 基本替換
println!("分數: {:>10}", score); // 靠右對齊,寬度 10
println!("分數: {:<10}", score); // 靠左對齊
println!("浮點: {:.2}", 3.14159); // 保留 2 位小數
println!("Debug: {:?}", [1, 2, 3]); // Debug 輸出
println!("十六進位: {:x}", 255); // ff
println!("二進位: {:b}", 10); // 1010
練習題說明
# 練習 1:溫度轉換器
cargo run -p phase0_basics --example ex01_temperature
# 練習 2:費氏數列產生器
cargo run -p phase0_basics --example ex02_fibonacci
# 練習 3:猜數字遊戲
cargo run -p phase0_basics --example ex03_guessing_game
# 執行所有單元測試
cargo test -p phase0_basics
重點複習
| 概念 | Rust | Java/Python 對比 |
|------|------|-----------------|
| 整數預設 | i32 | Java: int, Python: int(無限精度) |
| 浮點預設 | f64 | Java: double, Python: float |
| char 大小 | 4 bytes (Unicode) | Java: 2 bytes (UTF-16) |
| 變數可變性 | 預設不可變(let) | Java/Python 預設可變 |
| 函式回傳 | 最後表達式(無分號) | 必須用 return |
| if | 是表達式,可回傳值 | Java 中 if 是語句 |