สรุป The Rust Programming Language ฉบับมือเก่า ตอนที่ 10.2
2 min read Just now
บทความนี้สรุปจากหนังสือ The Rust Programming Language chapter 10.2 ด้วยความคิดเห็นของผมเอง อาจมีการตีความที่มีโอกาสผิดพลาดได้ ทางที่ดีถ้าอยากได้ reference จริงๆแนะนำต้นทางครับ
Traits: Defining Shared Behavior
บทที่ 10.2 นี้เป็นเรื่อง traits
ที่ผมเองก็มีภาพในหัวของมันมาก่อนอ่านแล้วล่ะ เพราะก็คุ้นเคยกับ pattern แนวนี้มาพอสมควร ครั้งแรกที่เห็นคำนี้ใน Rust ผมก็นึกถึง traits
ใน scala ที่ผมคิดว่าก็คงจะไม่ต่างกันมากนัก (ในเชิง concept)
ผมสรุปได้ตามนี้แล้วกัน
traits
จะคล้ายๆinterfaces
ในภาษา OOP แต่จะไม่เชิงเหมือนซะทีเดียว- ผมคิดว่ามือเก่าหลายๆคนน่าจะคุ้นกับ
interfaces
กันอยู่แล้ว งั้นผมขอสรุปแบบสั้นๆ (ซึ่งไม่ถูก 100%) ว่ามันคือ interface ที่สามารถมี default implementation ได้ก็แล้วกัน 555 (ซึ่งจริงๆ interface บางภาษาก็ทำอย่างนั้นได้อยู่แล้ว) - define trait ง่ายมาก
pub trait TraitName {
fn a_method(&self) -> String;
}
- แล้วก็ implement trait ให้กับ type ใดๆได้ง่ายเหมือนกัน
impl TraitName for AType {
fn a_method(&self) -> String {
// body
}
}
- สามารถ define with default implementation ได้
pub trait TraitName {
fn a_method(&self) -> String {
// defuaul code
}
}
- ตอนรับเป็น param ใน function ก็ทำได้แบบนี้
pub fn a_func(item: &impl TraitName) {}
- หรือจะใช้เป็น constraint สำหรับ generic type param ก็ได้ (Rust เรียกว่า Trait Bound Syntax)
pub fn a_func<T: TraitName>(item: &T) {}
- อยากใส่ constraints มากกว่า 1 trait ก็สามารถใช้
+
operator ได้ง่ายๆเลย
pub fn a_func(item: &(impl TraitName + TraitName2)) {}
// หรือแบบ Trait Bound
pub fn a_func<T: TraitName + TraitName2>(item: &T) {}
- มีอีก alternative way คือการใช้ where clauses เพื่อ readability ในกรณีที่ตัว trait bounds เริ่มเยอะ
fn a_func<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
- แน่นอนว่าใช้ใน return type ก็ได้
fn a_func() -> impl TraitName {}
- อีกเรื่องที่น่าสนใจ ถ้ายังจำกันได้ตอนเรา associate function ให้กับ type เช่น
Pair<T>
สมมติเราบอกว่าimpl Pair<i32>
แบบนี้ method ในนั้นก็จะ available แค่กับPair<i32>
เท่านั้น เพราะเรา narrow down type ลงไปตรงนั้นแล้ว - ทีนี้เราสามารถใช้ Trait มาช่วย narrow down ได้เหมือนกัน
struct Pair<T> {
x: T,
y: T,
}
impl<T: TraitName> Pair<T> {
fn a_func(&self) -> Self {
//
}
}
- ในกรณีนี้
a_func
ก็จะ available แค่Pair<T>
ถ้า T implements TraitName เท่านั้น - หรือเราจะใช้ narrow down type ที่เราจะ implement trait ก็ยังได้!
impl<T: AnotherTrait> TraitName for T {
fn a_method(&self) -> String {
// body
}
}
- แบบนี้ code ข้างบนก็จะ implement
TraitName
ให้กับทุกๆ type ที่ implementAnotherTrait
:)
จบแล้วครับ 10.2