สรุป The Rust Programming Language ฉบับมือเก่า ตอนที่ 9

Kasama Chenkaow
2 min read3 days ago

--

บทความนี้สรุปจากหนังสือ The Rust Programming Language chapter 9 ด้วยความคิดเห็นของผมเอง อาจมีการตีความที่มีโอกาสผิดพลาดได้ ทางที่ดีถ้าอยากได้ reference จริงๆแนะนำต้นทางครับ

(Link ตอนที่แล้ว)

Error Handling

Chapter นี้พูดถึงการ handle error โดย Rust แบ่งเป็น recoverable กับ unrecoverable errors ซึ่งเป็นเรื่องที่ดีมากๆ เพราะทำให้เราไม่ต้องมานั่งแยกเองว่าเราควรจะ retry error แบบไหน

แน่นอนว่า unrecoverable ก็จะ panic ที่ runtime ส่วน recoverable จะ return Result<T, E> ออกมาให้

ตัวอย่าง code ที่ panic ก็อย่างเช่นเราพยายาม access element ที่ไม่มีของ array หรือ vector ด้วย indexing

เรายังสามารถ panic เองโดยเรียก panic!()

ส่วน type ของ Result ก็คือ

enum Result<T, E> {
Ok(T),
Err(E),
}

และ Result<T, E> มาพร้อมกับ prelude

ซึ่งเราก็น่าจะเดาได้แหล่ะเนอะว่าเอามา handle ด้วย match ได้แบบนี้

    let a_file = match result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {error:?}"),
};

แต่เรายังสามารถมี nested match เพื่อ handle ตาม type ของ error ได้แบบนี้ด้วย

use std::fs::File;
use std::io::ErrorKind;

fn main() {
let greeting_file_result = File::open("hello.txt");

let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {e:?}"),
},
other_error => {
panic!("Problem opening the file: {other_error:?}");
}
},
};
}

หรือใช้ unwrap_or_else ซึ่งถ้า Result ไม่มี error ก็จะ return ค่าออกมาให้ แต่ถ้าเป็น error ก็จะ invoke callback ที่เรา provide ให้ด้วย error ที่มีแบบนี้

use std::fs::File;
use std::io::ErrorKind;

fn main() {
let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {error:?}");
})
} else {
panic!("Problem opening the file: {error:?}");
}
});
}

หรือใช้ unwrap() แต่คราวนี้ถ้า Result เป็น error จะ panic แทน

ถ้าเราอยาก custom ตัว panic message ก็สามารถใช้ expect(message) แทน unwrap() ได้

Propagating Errors

ต่อมาก็คือเรื่องของการโยน error ใน function ไปให้ caller handle

ที่จริงมันก็คล้ายๆกับ unwrap() หรือ expect() แต่ในกรณีนั้นมันก็การ panic ที่ caller ไม่สามารถ handle อะไรได้แล้ว

แต่สมมติว่าเราต้องการ return Error กลับออกไปให้ caller เราจะสามารถทำได้ยังไง?

คิดแบบ naive ก็คงได้โค้ดประมาณนี้

fn a_func(id: &str) -> Result<SomeType, SomeError> {
match check(&id) {
Ok(result) => Ok(&id),
Err(e) => Err(e),
}
}

ซึ่ง Rust ก็มี operator ที่ช่วยให้ชีวิตเราดีขึ้นอย่าง ? แล้วโค้ดเราก็จะเหลือแค่นี้

fn a_func(id: &str) -> Result<SomeType, SomeError> {
check(&id)?;
}

แล้วทำงานเหมือนข้างบนเป๊ะ!

ข้อจำกัดก็คือมันทำงานได้เฉพาะใน function ที่ return Result หรือ Option หรือ type ที่ implement FromResidual (แล้วของที่ใช้ ? ด้วยก็ต้อง return type ตรงกับ function นั้นๆด้วยนะ เช่นในกรณีบน a_func returns Result ตัว check ก็ต้อง returns Result ถึงจะใช้ ? ได้ จะปน Result กับ Option ไม่ได้ ไม่งั้นต้องมา match เอง)

ส่วนบทสุดท้ายก็จะเป็นการพูดถึงเรื่องว่าเมื่อไหร่เราควรจะ return Result หรือ panic

เค้าบอกว่า โดยทั่วๆไปเราอยากจะ return Result เพราะ caller สามารถเลือกเองได้ว่าจะทำยังไงต่อกับ error แต่ก็มีบางกรณีที่เราอยากจะ panic เช่น

  • เราไม่อยากให้ user retry operation เพราะเหตุผลด้าน security (เค้ายกตัวอย่างการพยายาม access out of bound element ใน array เพราะสามารถไปอ่านข้อมูลของ memory นอก array นั้นได้)
  • เราอยากขึ้นพวก example หรือ demo
  • เราเขียน test (เพราะส่วนใหญ่เราอยาก fail fast)
  • เรารู้ดีกว่า program มันไม่มีวัน error หรอก (ก็เลยไม่ต้อง return Result มา handle error ให้เสียเวลา)

อาจจะมีอย่างอื่นที่ผมอ่านข้ามไปอีก ถ้าอยากอ่านทั้งหมดก็ตามเนื้อหาในหนังสือเลยครับ

จบแล้วครับบทที่ 9

--

--

No responses yet