နားလည်ထားရမယ့် အချက်
Generics မရှိရင် largest value ရှာတဲ့ function တစ်ခုကို `i32` အတွက်တစ်ခု, `f64` အတွက်တစ်ခု, `String` အတွက်တစ်ခု ဆိုပြီး duplicate ရေးနေရမှာဖြစ်ပါတယ်—generic type parameter (`fn largest<T: PartialOrd>(list: &[T]) -> &T`) ကို သုံးရင် logic တစ်ခုတည်းကို type အမျိုးမျိုးအတွက် တစ်ကြိမ်တည်းသာ ရေးရုံနှင့် လုံလောက်ပါတယ်—`T` ဆိုတာ compile time မှာသာ concrete type အဖြစ် ဖြေရှင်းမည့် placeholder ဖြစ်ပါတယ်။ Java/TypeScript ရဲ့ generic တွေက runtime မှာ type erasure ဖြစ်ပြီး (compiled code ထဲ generic info ကျန်မနေတော့) run လုပ်ချိန်မှာ type check လုပ်ရပေမယ့် Rust ရဲ့ generic ကတော့ လုံးဝ ကွာပါတယ်—Rust compiler က generic function တစ်ခုကို concrete type (`i32`, `f64`, `String`) အသီးသီးအတွက် compile time မှာ specific version တွေအဖြစ် "monomorphization" ဆိုတဲ့ process ကနေ auto-generate ပေးလိုက်ပါတယ်—ဒါကြောင့် generic code ဟာ hand-written duplicate code များစွာနှင့် performance အတိအကျ တူညီပြီး runtime overhead လုံးဝမရှိဘူး (zero-cost abstraction)။ Generic type parameter ကို trait bound (`T: PartialOrd`, Lesson 14 မှာ ပိုနက်ရှိုင်းစွာလေ့လာမယ်) ဖြင့် constrain မလုပ်ရင် `T` ရဲ့ behavior ဘာမှ သိမ်ထားလို့ compiler က ဘာ operation မှ (`>` comparison ကိုတောင်) ခွင့်မပြုပေး—generic type ကို "ဘာမဆို လက်ခံ" လို့ မထင်သင့်ဘဲ trait bound က ဘာ operation တွေ actually available လဲ ဆိုတာ ကို ဖော်ပြနေတာလို့ မှတ်ထားရပါမယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Content analyzer ရဲ့ "list ထဲက maximum value ရှာ" logic ကို `fn find_max_stat<T: PartialOrd>(items: &[T]) -> Option<&T>` generic function တစ်ခုတည်းအဖြစ် ရေးမယ်—word count (`u32`) list တွေ၊ average sentence length (`f64`) list တွေ နှစ်မျိုးလုံးအတွက် ဒီ function တစ်ခုတည်းကို ပြန်လည်အသုံးပြုနိုင်ပါတယ်။ Monomorphization ကြောင့် `find_max_stat::<u32>` version နှင့် `find_max_stat::<f64>` version နှစ်ခု compile time မှာ သီးခြား generate ဖြစ်သွားမှာဖြစ်လို့ runtime မှာ performance loss ဘာမှ ရှိမှာ မဟုတ်ပါဘူး—hand-written duplicate function နှစ်ခု ရေးသလို ပါပဲ။ Generic `Stats<T>` struct တစ်ခုကို min/max/average ကို hold ဖို့ ဒီဇိုင်းလုပ်ချင်ရင် `T: PartialOrd + Copy` trait bound ကို သတ်မှတ်ပေးရမယ်—ဒီလို bound မထားရင် compiler က min/max comparison operation ကို လက်ခံမှာ မဟုတ်ပါဘူး။
အတူတူ စမ်းရေးကြည့်မယ်
fn find_max_stat<T: PartialOrd>(items: &[T]) -> Option<&T> {
let mut max = items.first()?;
for item in items {
if item > max {
max = item;
}
}
Some(max)
}
fn main() {
let word_counts = [420, 890, 615, 1200];
let avg_lengths = [4.2, 5.8, 3.9];
println!("{:?}", find_max_stat(&word_counts));
println!("{:?}", find_max_stat(&avg_lengths));
}`Some(1200)` နှင့် `Some(5.8)` ကို print ထုတ်နိုင်မည်—function တစ်ခုတည်းက type နှစ်မျိုးလုံးအတွက် အလုပ်လုပ်ကြောင်း သက်သေပြမည်။၅ မိနစ် စမ်းကြည့်
`fn average<T: Into<f64> + Copy>(items: &[T]) -> f64` generic function ရေးပါ—list ရဲ့ average ကို f64 အနေနဲ့ ပြန်ပေးရမည်—`u32` list နှင့် `i32` list နှစ်မျိုးလုံးနှင့် test ကြည့်ပါ။
သတိလေးတစ်ချက်
Generic function တစ်ခုကို trait bound လုံးဝမပါဘဲ `fn largest<T>(list: &[T]) -> T` ရေးပြီး `>` comparison operation ကို ခေါ်ကြိုးစားခြင်း—compiler က "binary operation `>` cannot be applied" error ပြပါလိမ့်မယ်—`T: PartialOrd` bound လိုအပ်ပါတယ်။
Generic code ကို runtime type-erasure ရှိတဲ့ Java generics လို "single shared implementation" လို့ ထင်ခြင်း—Rust monomorphization ကြောင့် type တစ်ခုစီအတွက် binary size ပိုကြီးလာနိုင်တာကို ("code bloat") မမျှော်လင့်ဘဲ ရှိတတ်ပါတယ်။