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

ประเภท Option

หลายภาษาอนุญาตให้ตัวแปรใดก็ตามเก็บค่า “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 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 ต้องครบถ้วน

เมื่อคุณสนใจแค่กรณี Some if let กระชับกว่า:

if let Some(value) = divide(10.0, 2.0) {
println!("Got: {}", value);
}

ส่วน body จะ execute เฉพาะเมื่อค่าเป็น Some เพิ่ม else เพื่อจัดการ None ถ้าต้องการ

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 ใช้ 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); // None

map จำเป็นสำหรับการ 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);
}
Option<T> ใน Rust แทนอะไร?
unwrap_or(default) ทำอะไรเมื่อเรียกกับ None?
map ทำอะไรเมื่อเรียกกับค่า None?
ทำไม Rust จึงใช้ Option<T> แทน null?