สรุป The Rust Programming Language ฉบับมือเก่า ตอนที่ 10.2

Kasama Chenkaow
2 min readJust now

--

บทความนี้สรุปจากหนังสือ The Rust Programming Language chapter 10.2 ด้วยความคิดเห็นของผมเอง อาจมีการตีความที่มีโอกาสผิดพลาดได้ ทางที่ดีถ้าอยากได้ reference จริงๆแนะนำต้นทางครับ

(Link ตอนที่แล้ว)

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 ที่ implement AnotherTrait :)

จบแล้วครับ 10.2

--

--

No responses yet