နားလည်ထားရမယ့် အချက်
Lesson 6 (ownership move), Lesson 7 (borrow rule: mutable ONE OR immutable MANY), Lesson 15 (lifetime) မှာ ကြားခဲ့တဲ့ error pattern များကို ဒီ exercise ထဲမှာ theory အနေနဲ့ ပြန်ရွတ်ပြရုံ မဟုတ်ဘဲ real broken code ထဲက symptom ကို ကိုယ်တိုင်ဖော်ထုတ် ပြင်ဆင်နိုင်ဖို့ debugging skill အဖြစ် practice လုပ်တာပါ။ Rust compiler ရဲ့ error message တွေဟာ အခြား language တွေထက် ပိုပြီး helpful ဖြစ်လေ့ရှိပါတယ်—"cannot borrow as mutable because it is also borrowed as immutable" (Lesson 7 rule ချိုးဖောက်), "value borrowed here after move" (Lesson 6 move ပြီးမှ ထပ်သုံး), "missing lifetime specifier" (Lesson 15 lifetime annotation လိုအပ်) စတာတွေက ဘယ် rule ကို ချိုးဖောက်ခဲ့သလဲ ရှင်းရှင်းလင်းလင်း ညွှန်ပြပေးလေ့ရှိပါတယ်—error message ကို အသေအချာ ဖတ်ခြင်းက fix ကို ရှာဖွေဖို့ ပထမဆုံး step ဖြစ်သင့်ပါတယ်။ Fix ချမယ့်နည်းလမ်း အများစုက pattern သုံးမျိုးထဲက တစ်ခုခုကို ရွေးချယ်တာဖြစ်ပါတယ်—(1) ownership move ကို borrow (`&`) ဖြင့် ပြောင်းအစားထိုးခြင်း, (2) `.clone()` ဖြင့် explicit deep copy ခေါ်ခြင်း, (3) scope ကို ချုံ့ပြီး conflicting borrow တွေကို overlap မဖြစ်အောင် ပြန်ဆောင်ရွက်ခြင်း—fix တစ်ခုစီရဲ့ tradeoff (performance cost vs code complexity) ကို နားလည်ထားရပါမယ်။ Borrow checker error ကို "အနှောင့်အယှက်" လို့ မမြင်ဘဲ—compile time မှာသာ တွေ့တဲ့ bug တစ်ခုက runtime crash (production ထဲ) ဖြစ်လာနိုင်ခြေကို ကာကွယ်ပေးနေတာလို့ mindset ပြောင်းထားသင့်ပါတယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Content analyzer codebase ကနေ real-world scenario သုံးခု ယူပါမယ်—(1) `fn print_and_return(s: String) -> String { println!("{s}"); s }` ကို ခေါ်ပြီးနောက် original variable ကို ဆက်သုံးကြိုးစားမယ့် code—move error ကို `&str` parameter ပြောင်းခြင်း (သို့) `.clone()` ဖြင့် ဖြေရှင်းရမယ်။ (2) `Vec<String>` တစ်ခုကို `for item in &vec { vec.push(...) }` လို loop ထဲကနေ ပြင်ဆင်ကြိုးစားမယ့် code—iteration ကိုယ်တိုင်က immutable borrow ဖြစ်နေလို့ ဒီ loop ထဲမှာ push မလုပ်နိုင်ဘူး—loop ပြီးမှ push လုပ်ရမယ့် pattern ဆီ ပြောင်းရမယ်။ (3) `fn shortest<'a>(a: &str, b: &'a str) -> &'a str` ကနေ lifetime annotation missing ဖြစ်နေတဲ့ code—`'a`/`'b` သီးခြားလိုအပ်ကြောင်း (သို့) parameter နှစ်ခုစလုံး lifetime တူညီအောင် ပြင်ဆင်ရမယ်။
အတူတူ စမ်းရေးကြည့်မယ်
// Snippet 1: fix the move error
fn print_and_return(s: String) -> String {
println!("{s}");
s
}
fn main() {
let title = String::from("Ownership");
let _ = print_and_return(title);
// println!("{title}"); // does not compile: value moved
// Snippet 2: fix the mutable-borrow-while-iterating error
let mut tags = vec![String::from("rust")];
// for t in &tags { tags.push(t.clone()); } // does not compile
// Snippet 3: fix the missing lifetime relationship
// fn shortest(a: &str, b: &str) -> &str { if a.len() < b.len() { a } else { b } }
}Snippet သုံးခုစလုံးကို compile အောင်မြင်အောင် ပြင်ဆင်ပြီး ဘယ် rule ကို ဘယ်လို ချိုးဖောက်ခဲ့လဲ တစ်ကြောင်းစီ ရှင်းပြနိုင်မည်။၅ မိနစ် စမ်းကြည့်
အပေါ်က snippet သုံးခုစလုံးကို compile ဖြစ်အောင် ပြင်ဆင်ပါ—snippet တစ်ခုစီအတွက် fix ရွေးချယ်ရာမှာ ဘာကြောင့် ဒီ fix ကို ရွေးလဲ (clone vs borrow vs scope ချုံ့) တစ်ကြောင်းစီ ရေးပါ။
သတိလေးတစ်ချက်
Borrow checker error ကို ဖြေရှင်းချင်တဲ့အခါ error message ကို မဖတ်ဘဲ `.clone()` ကို everywhere blanket ထည့်ကြိုးစားခြင်း—compile ရင် ရနိုင်ပေမယ့် root cause ကို နားမလည်ဘဲ unnecessary performance cost ကို ထပ်ထည့်နေတာဖြစ်နိုင်ပါတယ်။
Loop ထဲကနေ collection ကိုယ်တိုင် (`vec.push()`) ပြောင်းလဲကြိုးစားတဲ့ error ကို scope ချုံ့တဲ့ fix အစား `unsafe` block ဖြင့် borrow checker ကို bypass ကြိုးစားခြင်း—ဒါက Rust ရဲ့ core safety guarantee ကို ချိုးဖောက်တာဖြစ်လို့ absolutely ရှောင်ရမည်။
The Rust Programming Language — References and Borrowing — Rust