Impl & Trait
Impl
note
Impl หรือ Implementation คือการกำหนดความสามารถเพิ่มเติมให้กับตัวแปรชนิดต่างๆ เช่น struct หรือ enum โดยการกำหนด method เพิ่มเติมให้กับตัวแปรนั้นๆ
Example impl
struct Rectangle { width: u32, height: u32, } impl Rectangle { // สร้าง Instance ใหม่ของ struct นี้ fn new(width: u32, height: u32) -> Self { Self { width, height } } // เราสามารถกำหนดความสามารถเพิ่มเติมให้กับ struct ได้ // ในที่นี้เรากำหนดความสามารถในการหาพื้นที่ของรูปสี่เหลี่ยมผ่าน method area // &self คือการอ้างอิงตัวเอง fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect = Rectangle::new(30, 50); println!("Area of rectangle is {}", rect.area()); }
Trait
note
Trait คือ กลุ่มของคุณลักษณะ (methods) ที่กำหนดไว้ล่วงหน้า สามารถนำ impl ให้กับ struct หรือ enum เพื่อให้ struct หรือ enum เหล่านั้นมีความสามารถตามที่กำหนดใน trait เหล่านั้นได้
Example trait
// กำหนด trait คุณลักษณะที่ Animal มีร่วมกัน trait Animal { fn eat(&self, food: &str) -> (); fn can_make_sound(&self) -> bool; // เราสามารถกำหนดค่า Default ให้กับ method ได้ fn can_sleep(&self) -> bool { true } } struct Dog; struct Cat; // นำ Trait ไปใช้กับ struct // เราจำเป็นต้อง implement ทุก method ที่ไม่มีค่า Default ใน trait นั้นๆ impl Animal for Dog { fn eat(&self, food: &str) -> () { println!("Dog eat {}", food); } fn can_make_sound(&self) -> bool { true } } impl Animal for Cat { fn eat(&self, food: &str) -> () { println!("Cat eat {}", food); } fn can_make_sound(&self) -> bool { false } // สามารถ Override ค่า Default ได้ fn can_sleep(&self) -> bool { false } } fn main() { let dog = Dog; let cat = Cat; dog.eat("Bone"); cat.eat("Fish"); println!("Dog can make sound: {}", dog.can_make_sound()); println!("Cat can make sound: {}", cat.can_make_sound()); println!("Can dog sleep: {}", dog.can_sleep()); println!("Can cat sleep: {}", cat.can_sleep()); }
Inheritance Trait
note
เราสามารถสืบทอดคุณลักษณะของ Trait อื่นๆ ให้กับ Trait ที่เราสร้างได้โดย Syntax นี้:
trait ChildTrait: ParentTrait_1 + ParentTrait_2 + ...
tip
การ inheritance trait ChildTrait: ParentTrait_1 + ParentTrait_2 + ...
นี้ไม่ได้หมายความว่า ChildTrait
จะมีคุณลักษณะของ ParentTrait
ทั้งหมด แต่หมายความว่า
ก่อนจะสามารถใช้ ChildTrait
ได้ จะต้องมีคุณลักษณะของ ParentTrait
ทั้งหมดก่อนเสมอ
Example inheritance trait
trait Animal { fn eat(&self, food: &str) -> (); fn can_make_sound(&self) -> bool; } trait SomeOtherTrait { fn some_other_method(&self) -> (); } trait DogTrait: Animal + SomeOtherTrait { fn bark(&self) -> (); } struct Dog; impl DogTrait for Dog { fn eat(&self, food: &str) -> () { println!("Dog eat {}", food); } fn can_make_sound(&self) -> bool { true } fn bark(&self) -> () { println!("Dog bark"); } } fn main() { let dog = Dog; dog.eat("Bone"); dog.bark(); }
Genric Trait Bound
note
เราสามารถใช้ประโยชน์ของ Trait ในการกำหนดคุณลักษณะของ Function ได้โดยการใช้
Generic Trait Bound
เพื่อระบุว่า argument
หรือ return type
ต้องมีคุณลักษณะอะไรบ้าง
tip
การใช้ Generic Trait Bound
นี้จะทำให้เรามีความยืดหยุ่นในการเขียนโปรแกรมได้มากขึ้น
โดยเราสามารถเขียนได้ 3 วิธี ดังนี้:
fn function_name<T: TraitName>(arg: T) -> T
fn function_name<T>(arg: T) -> T where T: TraitName
fn function_name(arg: impl TraitName) -> impl TraitName
Example Generic Trait Bound
trait Animal { fn eat(&self, food: &str) -> (); } struct Dog; struct Cat; impl Animal for Dog { fn eat(&self, food: &str) -> () { println!("Dog eat {}", food); } } impl Animal for Cat { fn eat(&self, food: &str) -> () { println!("Cat eat {}", food); } } fn main() { let dog = get_animal("Dog"); let cat = get_animal("Cat"); feed_animal(&dog); feed_animal(&cat); } fn get_animal(type: &str) -> impl Animal { fn get_animal(type: &str) -> impl Animal { if type == "Dog" { Dog } else { Cat } } fn feed_animal(animal: &impl Animal) { animal.eat("Food"); }