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

ประเภทข้อผิดพลาดแบบกำหนดเอง

การใช้ String หรือ Box<dyn std::error::Error> เป็นประเภทข้อผิดพลาดของคุณใช้ได้ดีสำหรับสคริปต์ด่วน แต่ไลบรารีและแอปพลิเคชันจริงได้ประโยชน์จาก enum ข้อผิดพลาดที่เป็น concrete:

  • ผู้เรียกสามารถ pattern-match บน variant เฉพาะและจัดการแต่ละกรณีต่างกัน
  • ประเภทข้อผิดพลาดเป็นเอกสารทุก failure mode ของ API ของคุณ
  • คุณสามารถแนบข้อมูลที่มีโครงสร้าง (เช่น ค่าที่ไม่ถูกต้อง) ให้กับแต่ละ variant

ประกาศ enum ที่มีหนึ่ง variant ต่อหนึ่ง failure mode derive Debug เพื่อให้ข้อผิดพลาดสามารถพิมพ์ด้วย {:?}:

#[derive(Debug)]
enum AppError {
ParseError(std::num::ParseIntError),
NegativeNumber(i32),
TooBig(i32),
}

trait std::fmt::Display ควบคุมข้อความข้อผิดพลาดที่อ่านได้โดยมนุษย์ — สิ่งที่คุณเห็นเมื่อพิมพ์ด้วย {}:

use std::fmt;
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::ParseError(e) => write!(f, "parse error: {}", e),
AppError::NegativeNumber(n) => write!(f, "number is negative: {}", n),
AppError::TooBig(n) => write!(f, "number is too big: {} (max 100)", n),
}
}
}

เพื่อให้ใช้ได้ทุกที่ที่คาดหวัง dyn Error (เช่น ใน Box<dyn Error>) ประเภทของคุณต้อง implement std::error::Error trait นี้ไม่มีเมธอดที่ต้องการ — การ implement เริ่มต้นเพียงพอสำหรับกรณีส่วนใหญ่ เลือก implement source() เพื่อเปิดเผยสาเหตุที่อยู่เบื้องหลัง:

impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::ParseError(e) => Some(e),
_ => None,
}
}
}

การ implement From<SomeOtherError> for AppError ให้ตัวดำเนินการ ? แปลงโดยอัตโนมัติเมื่อเรียกฟังก์ชันที่คืน SomeOtherError:

impl From<std::num::ParseIntError> for AppError {
fn from(e: std::num::ParseIntError) -> Self {
AppError::ParseError(e)
}
}

ตอนนี้ s.parse::<i32>()? ในฟังก์ชันที่คืน Result<_, AppError> จะห่อ ParseIntError เป็น AppError::ParseError โดยอัตโนมัติ

use std::fmt;
use std::num::ParseIntError;
#[derive(Debug)]
enum AppError {
ParseError(ParseIntError),
NegativeNumber(i32),
TooBig(i32),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::ParseError(e) => write!(f, "parse error: {}", e),
AppError::NegativeNumber(n) => write!(f, "number is negative: {}", n),
AppError::TooBig(n) => write!(f, "number is too big: {} (max 100)", n),
}
}
}
impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::ParseError(e) => Some(e),
_ => None,
}
}
}
impl From<ParseIntError> for AppError {
fn from(e: ParseIntError) -> Self {
AppError::ParseError(e)
}
}
fn parse_bounded(s: &str) -> Result<i32, AppError> {
let n: i32 = s.parse()?; // ParseIntError -> AppError via From
if n < 0 { return Err(AppError::NegativeNumber(n)); }
if n > 100 { return Err(AppError::TooBig(n)); }
Ok(n)
}
fn main() {
let cases = ["42", "-5", "200", "abc"];
for s in &cases {
match parse_bounded(s) {
Ok(n) => println!("{} -> Ok({})", s, n),
Err(e) => println!("{} -> Err: {}", s, e),
}
}
}
trait ใดที่ให้ข้อความข้อผิดพลาดที่อ่านได้โดยมนุษย์ที่แสดงด้วย {}?
จุดประสงค์ของการ implement From<OtherError> for MyError คืออะไร?
source() คืนอะไรใน std::error::Error?
เหตุใด enum ข้อผิดพลาดที่เป็น concrete จึงดีกว่า Box<dyn Error> สำหรับ API ของไลบรารี?