ประเภท Option
Rust ไม่มี Null
หัวข้อที่มีชื่อว่า “Rust ไม่มี Null”หลายภาษาอนุญาตให้ตัวแปรใดก็ตามเก็บค่า “null” หรือ “nil” ซึ่งนำไปสู่ null-pointer exception ใน runtime Rust ใช้วิธีที่แตกต่าง: ไม่มี null เมื่อค่าอาจขาดหาย คุณใช้ Option<T> จาก standard library
Option<T> คือ enum ที่กำหนดเป็น:
enum Option<T> { Some(T), None,}Some(value) ห่อหุ้มค่าที่มีอยู่ประเภท T None แทนการขาดหายของค่า คอมไพเลอร์บังคับให้คุณจัดการทั้งสองกรณีก่อนจะใช้ค่าภายใน — ข้อผิดพลาด null-pointer กลายเป็น compile-time error
เนื่องจาก Option<T> ใช้กันทั่วไปมาก Some และ None จึงอยู่ใน scope ทุกที่โดยไม่ต้องใช้ prefix Option::
จัดการ Option<T> ด้วย match
หัวข้อที่มีชื่อว่า “จัดการ Option<T> ด้วย match”วิธีที่ชัดเจนที่สุดในการจัดการ Option<T> คือ match expression:
fn divide(a: f64, b: f64) -> Option<f64> { if b == 0.0 { None } else { Some(a / b) }}
match divide(10.0, 2.0) { Some(result) => println!("Result: {}", result), None => println!("Cannot divide by zero"),}คอมไพเลอร์ปฏิเสธโค้ดที่จัดการแค่ Some โดยไม่มี None — match ต้องครบถ้วน
if let สำหรับ Happy Path
หัวข้อที่มีชื่อว่า “if let สำหรับ Happy Path”เมื่อคุณสนใจแค่กรณี Some if let กระชับกว่า:
if let Some(value) = divide(10.0, 2.0) { println!("Got: {}", value);}ส่วน body จะ execute เฉพาะเมื่อค่าเป็น Some เพิ่ม else เพื่อจัดการ None ถ้าต้องการ
unwrap_or — ให้ค่า Default
หัวข้อที่มีชื่อว่า “unwrap_or — ให้ค่า Default”unwrap_or(default) คืนค่าภายในหาก Some หรือค่า default ที่กำหนดหาก None:
let maybe: Option<i32> = None;let value = maybe.unwrap_or(0); // 0นี่คือรูปแบบที่ปลอดภัยที่สุดของ “ให้ค่าหรือ fallback” หลีกเลี่ยง .unwrap() แบบธรรมดาใน production code เพราะ panic กับ None
map — แปลงค่าภายใน
หัวข้อที่มีชื่อว่า “map — แปลงค่าภายใน”map ใช้ closure กับค่า Some และคืน Option<T> ใหม่ หากค่าเดิมเป็น None map จะส่ง None ผ่านไปโดยไม่เปลี่ยนแปลง:
let doubled = Some(5).map(|x| x * 2); // Some(10)let nothing: Option<i32> = None;let still_none = nothing.map(|x| x * 2); // Nonemap จำเป็นสำหรับการ chain การแปลงโดยไม่ต้อง unwrap ค่า Option<T> ที่อยู่ระหว่างกลาง
fn divide(a: f64, b: f64) -> Option<f64> { if b == 0.0 { None } else { Some(a / b) }}
fn find_first_even(nums: &[i32]) -> Option<i32> { nums.iter().find(|&&x| x % 2 == 0).copied()}
fn main() { // match on Option match divide(10.0, 3.0) { Some(result) => println!("10 / 3 = {:.4}", result), None => println!("Cannot divide by zero"), }
match divide(5.0, 0.0) { Some(result) => println!("result = {}", result), None => println!("Cannot divide by zero"), }
// if let let numbers = vec![1, 3, 5, 8, 9]; if let Some(even) = find_first_even(&numbers) { println!("First even: {}", even); }
// unwrap_or let missing: Option<i32> = None; println!("Default: {}", missing.unwrap_or(0));
// map let some_val: Option<i32> = Some(5); let doubled = some_val.map(|x| x * 2); println!("Doubled: {:?}", doubled);}Compiling…