Thuta Learning
ရှာဖွေရန်
Rust
ExercisesProgrammingbeginner

Exercise — Error-Handling Strategy ဒီဇိုင်းဆွဲခြင်း

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • Exercise — Error-Handling Strategy ဒီဇိုင်းဆွဲခြင်း concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • နမူနာ Rust code ကို ကိုယ်တိုင် run ပြီး output စစ်နိုင်ရန်
  • Tutorial Platform project နှင့် production scenario တွင် မှန်ကန်စွာအသုံးချနိုင်ရန်

နားလည်ထားရမယ့် အချက်

Lesson 11 ရဲ့ `Result<T, E>` ကို real project တစ်ခုမှာ သုံးတဲ့အခါ error type `E` ကို ဘယ်လို ဒီဇိုင်းဆွဲမလဲဆိုတာ practical decision တစ်ခုဖြစ်ပါတယ်—function တစ်ခုစီက error source ကွဲပြားစွာ (`io::Error` file read, custom parse error, validation error) ရှိနေရင် ဒီ error type အားလုံးကို represent ပေးနိုင်တဲ့ custom `enum AnalyzerError { Io(io::Error), Parse(String), Validation(String) }` တစ်ခု ဒီဇိုင်းဆွဲသင့်ပါတယ်—ဒါက Lesson 10 ရဲ့ enum concept ကို error handling domain မှာ direct apply လုပ်တာပါ။ `std::error::Error` trait (Lesson 14 ရဲ့ trait concept) ကို ဒီ custom enum အပေါ် implement လုပ်ရင် (`impl std::error::Error for AnalyzerError {}` + `Display` trait) standard error handling ecosystem (logging library, `Box<dyn Error>` return type) နှင့် interoperable ဖြစ်လာပါတယ်—library/application code များစွာမှာ ဒီ pattern ကို standard အဖြစ် မျှော်လင့်ပါတယ်။ `From<io::Error> for AnalyzerError` ကို implement ထားရင် `?` operator ကို function boundary များကြား ဆက်တိုက်သုံးနိုင်ပါတယ်—inner function က `io::Error` ကို return ပေမယ့် outer function ရဲ့ error type က `AnalyzerError` ဆိုရင် `?` က automatic conversion လုပ်ပေးလို့ manual `.map_err(...)` ရေးစရာ မလိုအပ်တော့ပါ—ဒါက Lesson 11 ရဲ့ `?` operator ကို layer များစွာ ရှိတဲ့ real application ထဲမှာ ဘယ်လို scale ဖြစ်လဲ ဖော်ပြတဲ့ direct extension ပါ။ Custom error type ဒီဇိုင်းဆွဲတာဟာ library authoring skill ရဲ့ core တစ်ခုဖြစ်ပြီး—Rust ecosystem ထဲက popular crate (`thiserror`, `anyhow`) တွေက ဒီ boilerplate ကို လျှော့ချပေးတာသာ ဖြစ်ပြီး underlying concept (error type ကို data အဖြစ် explicit ဖော်ပြခြင်း) ကတော့ မပြောင်းလဲပါဘူး။

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

Content analyzer module အတွက် `enum AnalyzerError { Io(io::Error), EmptyFile(String) }` ဖန်တီးပြီး `Display` trait ကို implement မယ်—error message ကို human-readable ဖြစ်အောင် format ပေးမယ်။ `impl From<io::Error> for AnalyzerError { fn from(e: io::Error) -> Self { AnalyzerError::Io(e) } }` ကို ရေးမယ်—ဒါကနေ file-reading function (`fs::read_to_string(path)?`) ထဲက `?` operator က automatic conversion ရလာမယ်။ `fn analyze(path: &str) -> Result<u32, AnalyzerError> { let content = fs::read_to_string(path)?; if content.is_empty() { return Err(AnalyzerError::EmptyFile(path.to_string())); } Ok(count_words(&content)) }` ဖြင့် function layer နှစ်ခုကို ဆက်စပ်မယ်—`?` operator တစ်ခုတည်းက error type နှစ်မျိုးလုံးကို seamlessly ကိုင်တွယ်ပေးမှာ ဖြစ်ပါတယ်။

အတူတူ စမ်းရေးကြည့်မယ်

rust
use std::fmt;
use std::fs;
use std::io;

#[derive(Debug)]
enum AnalyzerError {
    Io(io::Error),
    EmptyFile(String),
}

impl fmt::Display for AnalyzerError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AnalyzerError::Io(e) => write!(f, "io error: {e}"),
            AnalyzerError::EmptyFile(path) => write!(f, "empty file: {path}"),
        }
    }
}

impl std::error::Error for AnalyzerError {}

impl From<io::Error> for AnalyzerError {
    fn from(e: io::Error) -> Self {
        AnalyzerError::Io(e)
    }
}

fn analyze(path: &str) -> Result<u32, AnalyzerError> {
    let content = fs::read_to_string(path)?;
    if content.is_empty() {
        return Err(AnalyzerError::EmptyFile(path.to_string()));
    }
    Ok(content.split_whitespace().count() as u32)
}

fn main() {
    match analyze("missing.md") {
        Ok(count) => println!("{count} words"),
        Err(e) => println!("error: {e}"),
    }
}
You should see
File မရှိရင် "error: io error: ..." ကို print ထုတ်နိုင်မည်—`?` operator က `io::Error` ကို `AnalyzerError` ဆီ automatic convert လုပ်ပေးခဲ့ကြောင်း သက်သေပြမည်။

၅ မိနစ် စမ်းကြည့်

`AnalyzerError` ထဲကို `TooManyWords(u32)` variant အသစ် ထပ်ထည့်ပါ—word count ကို 5000 ကျော်ရင် ဒီ error ကို return ပေးအောင် `analyze` function ကို ပြင်ပါ—`Display` implementation ထဲမှာ ဒီ variant အတွက် message ကိုပါ ထည့်ပါ။

သတိလေးတစ်ချက်

Error type ကို `String` တစ်မျိုးတည်း (ဥပမာ `Result<T, String>`) ဖြင့်သာ blanket သုံးခြင်း—caller ဘက်က error kind အလိုက် ကွဲပြားစွာ handle လုပ်ချင်ရင် (ဥပမာ retry logic ကို IO error အတွက်သာ run) string parsing ပြန်လုပ်ရမယ့် fragile design ဖြစ်နိုင်ပါတယ်—custom enum ကသာ ဒီ information ကို ထိန်းသိမ်းပေးပါတယ်။

`From<InnerError> for OuterError` ကို implement မလုပ်ဘဲ `?` operator ကို layer များစွာကြား တိုက်ရိုက်သုံးကြိုးစားခြင်း—error type mismatch compile error ရနိုင်ပါတယ်—`.map_err(AnalyzerError::Io)` ဖြင့် manual convert (သို့) `From` implement လိုအပ်ပါတယ်။

Standard Library — std::error::ErrorRust

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • Error type ကို `String` တစ်မျိုးတည်း (ဥပမာ `Result<T, String>`) ဖြင့်သာ blanket သုံးခြင်း—caller ဘက်က error kind အလိုက် ကွဲပြားစွာ handle လုပ်ချင်ရင် (ဥပမာ retry logic ကို IO error အတွက်သာ run) string parsing ပြန်လုပ်ရမယ့် fragile design ဖြစ်နိုင်ပါတယ်—custom enum ကသာ ဒီ information ကို ထိန်းသိမ်းပေးပါတယ်။
  • `From<InnerError> for OuterError` ကို implement မလုပ်ဘဲ `?` operator ကို layer များစွာကြား တိုက်ရိုက်သုံးကြိုးစားခြင်း—error type mismatch compile error ရနိုင်ပါတယ်—`.map_err(AnalyzerError::Io)` ဖြင့် manual convert (သို့) `From` implement လိုအပ်ပါတယ်။
  • နမူနာ code ကို production system ပေါ် တိုက်ရိုက်မစမ်းဘဲ local/test environment တွင် အရင်အတည်ပြုပါ။

လေ့ကျင့်ခန်း

`AnalyzerError` ထဲကို `TooManyWords(u32)` variant အသစ် ထပ်ထည့်ပါ—word count ကို 5000 ကျော်ရင် ဒီ error ကို return ပေးအောင် `analyze` function ကို ပြင်ပါ—`Display` implementation ထဲမှာ ဒီ variant အတွက် message ကိုပါ ထည့်ပါ။

You'll know it worked when: File မရှိရင် "error: io error: ..." ကို print ထုတ်နိုင်မည်—`?` operator က `io::Error` ကို `AnalyzerError` ဆီ automatic convert လုပ်ပေးခဲ့ကြောင်း သက်သေပြမည်။

Exercise — Error-Handling Strategy ဒီဇိုင်းဆွဲခြင်း | Thuta Learning