ข้ามไปยังเนื้อหา

Control Flow

Rust มีโครงสร้าง control-flow ที่คุณรู้จักดีอยู่แล้ว — if, while, for — แต่มีความแตกต่างสำคัญสองข้อ: if และ match เป็น expression ที่ให้ค่ากลับมา และ match เป็นแบบ exhaustive (compiler จะ error ถ้าคุณตกหล่นเคสใดเคสหนึ่ง)

ใน TypeScript คุณจะหันไปใช้ ternary ? : เมื่อต้องการค่าตามเงื่อนไข ส่วนใน Rust ตัว if เองเป็น expression — ทุก branch ต้องให้ค่า type เดียวกัน

// TypeScript
const label = score >= 60 ? "pass" : "fail";
// Rust
let label = if score >= 60 { "pass" } else { "fail" };

match ใน Rust เหมือน switch ของ TypeScript แต่เป็นแบบ exhaustive และ expression-oriented compiler รับประกันว่าทุกค่าที่เป็นไปได้ถูกจัดการ pattern สามารถเป็น range, literal, enum และอื่น ๆ ได้

  • loop รันไปเรื่อย ๆ จนกว่าจะเจอ break ต่างจาก while true ตรงที่ loop สามารถ return ค่ากลับมาได้ผ่าน break value
  • while ทำงานตามที่คาดหวัง
  • for item in collection วนซ้ำบนสิ่งที่ iterate ได้ใด ๆ — ไม่ต้องคำนวณ index เอง
TypeScript
const score = 85;
const grade = score >= 90 ? "A" : score >= 80 ? "B" : "C";
console.log(grade); // "B"
// switch (not exhaustive by default)
switch (score) {
case 100: console.log("perfect"); break;
default: console.log("ok");
}
Rust
fn main() {
let score = 85;
// if is an expression
let grade = if score >= 90 { "A" } else if score >= 80 { "B" } else { "C" };
println!("{grade}"); // B
// loop with a break value
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 5 { break counter * 2; }
};
println!("{result}"); // 10
// for over a range
for i in 1..=3 {
println!("{i}");
}
// match is exhaustive
match score {
90..=100 => println!("A"),
80..=89 => println!("B"),
_ => println!("C or below"),
}
}

ใน TypeScript คุณใช้ optional chaining กับ && เพื่อตรวจและ narrow type ในเงื่อนไขเดียวได้เลย ส่วน Rust เมื่อก่อนบังคับให้ซ้อน: if let วางได้แค่ติดกับ if เท่านั้น การตรวจเพิ่มอีกชั้นจึงหมายถึงการ indent เพิ่มอีกชั้น

ตั้งแต่ edition 2024 คุณต่อ let หลายตัวด้วย && ได้ และผสมกับเงื่อนไข boolean ธรรมดาได้ด้วย ตัวแปรที่ bind ไว้ใช้ได้ทั้งในเงื่อนไขถัดไปและใน body ส่วน let ต้องอยู่ระดับบนสุดของเงื่อนไข — ยังใช้ || คั่นระหว่างกันไม่ได้

TypeScript
const cfg = loadConfig();
// TypeScript narrows through optional chaining and &&
if (cfg?.retries != null && cfg.retries > 0) {
console.log(`retrying ${cfg.retries} times`);
}
Rust
// Before 2024 — nested
if let Some(c) = &cfg {
if let Some(n) = c.retries {
if n > 0 {
println!("retrying {n} times");
}
}
}
// 2024 edition — one condition
if let Some(c) = &cfg
&& let Some(n) = c.retries
&& n > 0
{
println!("retrying {n} times");
}

let chain ผูกกับ edition 2024 (stabilize ใน Rust 1.88) เพราะต้องพึ่งการเปลี่ยน temporary scope ของ if let ที่มาพร้อม edition นี้ ถ้าใช้ edition เก่ากว่า compiler จะ error ว่า “let chains are only allowed in Rust 2024 or later”

fn sum_first_two(nums: &[u8]) -> Option<u8> {
let mut iter = nums.iter();
if let Some(first) = iter.next()
&& let Some(second) = iter.next()
{
first.checked_add(*second)
} else {
None
}
}
#[derive(Debug)]
struct Config { retries: Option<u32> }
fn main() {
println!("{:?}", sum_first_two(&[3, 4, 5]));
println!("{:?}", sum_first_two(&[9]));
let cfg = Some(Config { retries: Some(3) });
// One condition, three checks — no nesting.
if let Some(c) = &cfg
&& let Some(n) = c.retries
&& n > 0
{
println!("retrying {n} times");
}
}
fn classify(n: i32) -> &'static str {
match n {
i32::MIN..=-1 => "negative",
0 => "zero",
1..=9 => "small positive",
_ => "large positive",
}
}
fn main() {
let score = 85;
let grade = if score >= 90 { "A" } else if score >= 80 { "B" } else { "C" };
println!("Grade: {grade}");
for n in [0, -3, 5, 42] {
println!("{n} is {}", classify(n));
}
}
เกิดอะไรขึ้นถ้าคุณลืมเคสใน expression `match` ของ Rust?
คุณ return ค่าจาก `loop` ใน Rust อย่างไร?
ทำไม let chain ต้องใช้ edition 2024?
ข้อใดต่อไปนี้เป็น Rust ที่ถูกต้อง?