Derive Macro
note
Derive Macroคือprocedural macroชนิดหนึ่งDerive Macroคือ shorthand impl Trait ให้กับตัวแปรชนิดต่างๆ เช่น struct หรือ enum โดยที่เราไม่ต้องเขียน impl Trait ด้วยตัวเองDerive Macroที่ได้ใช้บ่อยๆ มีดังนี้DebugPartialEqEqPartialOrdOrdCloneCopyDefault
warning
การ Derive ความสามารถให้กับ struct หรือ enum เกินความจำเป็นจะทำให้มีผลกระทบต่อ Performance เนื่องจากมีการ Allocate/Deallocate Heap Memory ที่สูงตามมา
Debug
note
ใช้สำหรับเพิ่มความสามารถในการ debug ค่าในตัวแปร เช่น struct หรือ enum ผ่าน
println!("{:?}", value) หรือ dbg!(value) โดยที่เราไม่ต้องเขียน impl Debug
ด้วยตัวเอง
Example Derive Debug
#[derive(Debug)] struct Rectangle { width: u32, height: u32, } fn main() { let rect1 = Rectangle { width: 30, height: 50, }; println!("rect1 is {:?}", &rect1); // หรือ dbg!(&rect1); }
Clone
note
ใช้สำหรับเพิ่มความสามารถในการ Clone ค่าของ struct หรือ enum ไปสร้างตัวแปรใหม่ ผ่าน
method .clone()
tip
การ Clone จะทำให้ตัวแปรที่ถูก Clone มีค่าเหมือนตัวแปรต้น แต่เป็นตัวแปรใหม่ ที่มี Ownership เป็นของตัวเอง
Example Derive Clone
#[derive(Clone)] struct Rectangle { width: u32, height: u32, } fn main() { let rect1 = Rectangle { width: 30, height: 50, }; let rect2 = rect1.clone(); // react1 จะยังสามารถใช้งานได้ เนื่องจากไม่มีการย้าย Ownership println!("rect1: {:?}", rect1); println!("rect2: {:?}", rect2); }
Copy
note
ใช้สำหรับเพิ่มความสามารถในการ Clone ค่าของ struct หรือ enum ไปยังตัวแปรอื่น
โดยอัตโนมัติ โดยไม่ใช้ Move Semantics (เหมือน Primitive Types)
warning
- ต้องใช้คู่กับ Derive
Cloneพร้อมกันเท่านั้น - ใช้เมื่อจำเป็นเท่านั้น เนื่องจากมี Cost ในการ Allocate/Deallocate Heap Memory ที่สูงตามมา ทำให้มีผลกระทบต่อ Performance
Example Derive Copy
#[derive(Clone, Copy)] struct Rectangle { width: u32, height: u32, } fn main() { let rect1 = Rectangle { width: 30, height: 50, }; // rect1 จะถูก Copy ไปยัง rect2 โดยอัตโนมัติ โดยไม่ต้องใช้ method `.clone()` // ซึ่งทำให้ rect1 ยังสามารถใช้งานได้ เนื่องจากไม่มีการย้าย Ownership let rect2 = rect1; // rect1 จะยังสามารถใช้งานได้ เนื่องจากไม่มีการย้าย Ownership println!("rect1: {:?}", rect1); println!("rect2: {:?}", rect2); }
PartialEq
note
- ใช้สำหรับเพิ่มความสามารถในการเปรียบเทียบความเท่ากัน
(== หรือ !=)ของ struct หรือ enum - อนุญาตให้มีค่าที่ไม่สามารถเปรียบเทียบกันได้ (เช่น NaN ใน floating-point number)
Eq
note
Eqเป็น trait ที่สืบทอดมาจากPartialEqEqรับประกันว่าความสัมพันธ์ความเท่ากันเป็นความสัมพันธ์สมมูล (equivalence relation) ทางคณิตศาสตร์ ดังนี้- สะท้อน (reflexive): สำหรับทุกค่า
a,a == aจะต้องเป็นจริง - สมมาตร (symmetric): สำหรับทุกค่า
aและb, ถ้าa == b,b == aจะต้องเป็นจริง - ถ่ายทอด (transitive): สำหรับทุกค่า
a,b, และc, ถ้าa == bและb == c,a == cจะต้องเป็นจริง
- สะท้อน (reflexive): สำหรับทุกค่า
warning
- ต้องใช้คู่กับ Derive
PartialEqพร้อมกันเท่านั้น - ไม่สามารถใช้กับ type ที่ไม่สามารถเปรียบเทียบกันได้ (เช่น NaN ใน floating-point number)
Example Derive PartialEq and Eq
// สามารถใช้ Eq ได้เนื่องจาก i32 มี equivalence relation #[derive(PartialEq, Eq)] struct RectangleInt { width: i32, height: i32, } // ไม่สามารถใช้ Eq ได้ เนื่องจาก f64 มีโอกาสเป็น NaN ซึ่งไม่สามารถเปรียบเทียบกันได้ #[derive(PartialEq)] struct RectangleFloat { width: f64, height: f64, } fn main() { let rect_int1 = RectangleInt { width: 30, height: 50, }; let rect_int2 = RectangleInt { width: 30, height: 50, }; let rect_float1 = RectangleFloat { width: f64::NAN, height: 50.0, }; let rect_float2 = RectangleFloat { width: f64::NAN, height: 50.0, }; // true println!("rect_int1 == rect_int2: {}", rect_int1 == rect_int2); // false เนื่องจาก NaN ไม่สามารถเปรียบเทียบกันได้ println!("rect_float1 == rect_float2: {}", rect_float1 == rect_float2); }
PartialOrd
note
- ใช้สำหรับเพิ่มความสามารถในการเปรียบเทียบแบบมีลำดับ (Ordering) ของ struct หรือ enum
ด้วย
<,>,<=,>= - จะเปรียบเทียบค่า
<,>,<=,>=ตามลำดับ property ของ struct หรือ enum และจะ return ค่า boolean จาก property แรกที่มีค่า<,>,<=,>=กับคู่ที่เปรียบเทียบ
warning
PartialOrdต้องการPartialEqเป็นพื้นฐาน
Example Derive PartialOrd
#[derive(PartialEq, PartialOrd)] struct Rectangle { width: u32, height: u32, } fn main() { let rect1 = Rectangle { width: 30, height: 50, }; let rect2 = Rectangle { width: 30, height: 50, }; println!("rect1 < rect2: {}", rect1 < rect2); }
Ord
note
Ordเป็น trait ที่สืบทอดมาจากPartialOrdและEq- รับประกันว่าทุกคู่ของค่าสามารถเปรียบเทียบกันได้และมีลำดับที่ชัดเจนเท่านั้น (Total ordering)
warning
- ต้องใช้คู่กับ Derive
PartialOrd,PartialEq, และEq - ไม่สามารถใช้กับ type ที่ไม่สามารถเปรียบเทียบกันได้ (เช่น NaN ใน floating-point number)
Example Derive PartialOrd and Ord
// สามารถใช้ Ord ได้เนื่องจาก i32 มี total ordering #[derive(PartialEq, PartialOrd, Eq, Ord)] struct RectangleInt { width: i32, height: i32, } // ไม่สามารถใช้ Eq ได้ เนื่องจาก f64 มีโอกาสเป็น NaN ซึ่งไม่สามารถเปรียบเทียบกันได้ #[derive(PartialEq, PartialOrd)] struct RectangleFloat { width: f64, height: f64, } fn main() { let rect_int1 = RectangleInt { width: 30, height: 50, }; let rect_int2 = RectangleInt { width: 30, height: 50, }; let rect_float1 = RectangleFloat { width: f64::NAN, height: 50.0, }; let rect_float2 = RectangleFloat { width: f64::NAN, height: 50.0, }; // true println!("rect_int1 < rect_int2: {}", rect_int1 < rect_int2); // false เนื่องจาก NaN ไม่สามารถเปรียบเทียบกันได้ println!("rect_float1 < rect_float2: {}", rect_float1 < rect_float2); }
Default
note
ใช้สำหรับเพิ่มความสามารถในการสร้าง struct หรือ enum
ตัวใหม่ให้มีค่าเป็นค่าเริ่มต้นของตัวแปรชนิดต่างๆ ผ่าน method ::default()
Example Derive Default
#[derive(Default)] struct Rectangle { width: u32, height: u32, } // หากเป็น enum จำเป็นต้องกำหนดค่า default เองด้วย `#[default]` attribute #[derive(Debug, Default, PartialEq)] enum Test { A, #[default] // กำหนดให้ B เป็นค่า default ของ enum Test B, C, } fn main() { let rect1 = Rectangle::default(); let test_enum = Test::default(); assert_eq!(rect1.width, 0); assert_eq!(rect1.height, 0); assert_eq!(test_enum, Test::B); }
Exercise
- ทำให้ struct
Userสามารถ Debug ได้
enum UserType { Admin, User, } struct User { name: String, age: u8, user_type: UserType, } fn main() { let user = User { name: "John Doe".to_string(), age: 20, user_type: UserType::Admin, }; println!("user: {:?}", user); }
- ทำให้ struct
Userสามารถเปรียบเทียบความเท่ากันได้ (==และ!=)
enum UserType { Admin, User, } struct User { name: String, age: u8, user_type: UserType, } fn main() { let user1 = User { name: "John Doe".to_string(), age: 20, user_type: UserType::Admin, }; let user2 = User { name: "Jane Doe".to_string(), age: 20, user_type: UserType::User, }; println!("user1 == user2: {}", user1 == user2); }
- ทำให้ struct
Userสามารถ Clone ได้
enum UserType { Admin, User, } struct User { name: String, age: u8, user_type: UserType, } fn main() { let user1 = User { name: "John Doe".to_string(), age: 20, user_type: UserType::Admin, }; let user2 = user1.clone(); println!("user1: {:?}", user1); println!("user2: {:?}", user2); }