နားလည်ထားရမယ့် အချက်
ဒီ project က course တစ်ခုလုံးရဲ့ Basic/Intermediate chapter နှစ်ခုကို capstone အနေနဲ့ ပေါင်းစည်းတဲ့ project ဖြစ်ပါတယ်—`std::env::args()` ကနေ command-line argument (directory path) ကို parse (Lesson 5 ရဲ့ control flow), directory ထဲက file တစ်ခုစီကို `Result<String, io::Error>` ဖြင့် ဖတ် (Lesson 11 ရဲ့ error handling), `LessonStat` struct များအဖြစ် accumulate (Lesson 9 ရဲ့ struct, Lesson 12 ရဲ့ `Vec`), နောက်ဆုံး report ကို print ထုတ်ခြင်း—layer လေးထပ်ကို တစ်ခုချင်းစီ ဒီဇိုင်းဆွဲရပါတယ်။ Real-world CLI tool တစ်ခုမှာ error handling strategy က အရေးကြီးဆုံးဖြစ်ပါတယ်—file တစ်ခု ဖတ်မရရင် (permission, corrupted) whole program ကို crash မဖြစ်စေချင်ဘဲ ဒီ file ကိုသာ skip ပြီး error ကို log ထားခြင်းဟာ Lesson 20 ရဲ့ "external input error = gracefully handle" strategy ရဲ့ practical application ဖြစ်ပါတယ်—`main` function တစ်ခုတည်းထဲမှာ `unwrap()` ချည်းသာ သုံးရင် file တစ်ခု fail ရုံနှင့် batch process တစ်ခုလုံး ရပ်တန့်သွားနိုင်ပါတယ်။ Ownership ကို efficient ဖြင့် manage ချင်ရင် file content ကို `String` (owned) အဖြစ်ဖတ်ပြီး `count_words(&content)` (Lesson 7 ရဲ့ borrow) ကို pass ရမယ်—content ကို function ဆီ move ပေးလိုက်ရင် ဒီ content ကနေ ထပ်ဆက် excerpt ထုတ်ချင်တဲ့အခါ ဆက်မရနိုင်တော့ပါ။ ဒီ project ကို run ကြည့်ပြီးမှသာ Lesson 6-13 ရဲ့ theory အားလုံးက production-shaped tool တစ်ခုထဲမှာ ဘယ်လို fit ဖြစ်လဲ ခံစားနိုင်ပါလိမ့်မယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
`fn main()` ထဲမှာ `std::env::args().nth(1)` ဖြင့် directory path argument ကို parse မယ်—argument မပေးထားရင် usage message print ပြီး `std::process::exit(1)` ဖြင့် ထွက်မယ်။ `std::fs::read_dir(path)?` ဖြင့် file list ကို ရယူပြီး file တစ်ခုစီအတွက် `match std::fs::read_to_string(entry.path()) { Ok(content) => { ... }, Err(e) => eprintln!("skip {path}: {e}") }` pattern ကို သုံးမယ်—file တစ်ခု fail ရင်တောင် ကျန်တဲ့ file တွေကို ဆက်process ဖို့ပါ။ File တစ်ခုချင်းစီအတွက် `LessonStat::new(file_name, count_words(&content))` ဖြင့် struct ဖန်တီးပြီး `Vec<LessonStat>` ထဲ push မယ်—အားလုံးပြီးရင် `HashMap<String, u32>` (difficulty label => count) ကို build ကာ summary report အနေနဲ့ print ထုတ်မယ်။
အတူတူ စမ်းရေးကြည့်မယ်
use std::collections::HashMap;
use std::fs;
struct LessonStat { name: String, word_count: u32 }
fn count_words(text: &str) -> u32 {
text.split_whitespace().count() as u32
}
fn main() {
let dir = std::env::args().nth(1).unwrap_or_else(|| ".".to_string());
let mut stats: Vec<LessonStat> = Vec::new();
if let Ok(entries) = fs::read_dir(&dir) {
for entry in entries.flatten() {
let path = entry.path();
match fs::read_to_string(&path) {
Ok(content) => {
let name = path.display().to_string();
stats.push(LessonStat { name, word_count: count_words(&content) });
}
Err(e) => eprintln!("skip {}: {e}", path.display()),
}
}
}
let mut total_by_bucket: HashMap<&str, u32> = HashMap::new();
for stat in &stats {
let bucket = if stat.word_count < 500 { "short" } else { "long" };
*total_by_bucket.entry(bucket).or_insert(0) += 1;
}
println!("{total_by_bucket:?}");
}Directory argument ပေးလိုက်ရင် ဒီ directory ထဲက file များအားလုံးရဲ့ word-count bucket summary (`{"short": N, "long": M}`) ကို print ထုတ်နိုင်မည်။၅ မိနစ် စမ်းကြည့်
အပေါ်က analyzer ကို extend လုပ်ပါ—file တစ်ခုစီရဲ့ longest line ကိုပါ track ပြီး "longest line overall" ကို final report ထဲ ထပ်ထည့်ပါ—file ဖတ်တဲ့အခါ error ဖြစ်ခဲ့တဲ့ file count ကိုလည်း report ထဲ ပြပါ။
သတိလေးတစ်ချက်
File ဖတ်တဲ့ loop ထဲမှာ `fs::read_to_string(&path).unwrap()` ကို သုံးခြင်း—file တစ်ခု (permission, corrupted encoding) fail ရင် batch process တစ်ခုလုံး crash ဖြစ်သွားနိုင်ပါတယ်—Err arm ကို gracefully handle ရပါမည်။
Directory argument ပေးလာမလား စစ်ဆေးစရာ မလိုဘူးလို့ ယူဆပြီး `args().nth(1).unwrap()` ကို directly ခေါ်ခြင်း—argument မပေးလာရင် panic ဖြစ်ပြီး user ကို helpful error message ပြောပြနိုင်မှာ မဟုတ်ပါ။