สรุป The Rust Programming Language ฉบับมือเก่า ตอนที่ 5
บทความนี้สรุปจากหนังสือ The Rust Programming Language chapter 5 ด้วยความคิดเห็นของผมเอง อาจมีการตีความที่มีโอกาสผิดพลาดได้ ทางที่ดีถ้าอยากได้ reference จริงๆแนะนำต้นทางครับ
บทนี้ ตัว Struct เองเป็นหนทางนึงในการ group พวก values ต่างๆเข้าด้วยกันเพื่อให้เกิด meaning ที่เราต้องการ ซึ่งถ้าเป็นมือเก่าก็น่าจะคุ้นๆมาจากภาษาอื่นมาพอสมควร
ตัวอย่าง struct
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
บทนี้พอจะ skimming ได้เยอะอยู่ แต่มี notable points อยู่นิดหน่อยที่ผมพอจะคิดได้
- Struct update -> เราสามารถสร้าง struct ใหม่จากของเก่าได้ด้วยโค้ดประมาณนี้
let user2 = User {
email: String::from("another@example.com"),
..user1
};
แต่ที่ต้องระวังคือถ้าบาง fields ถูกย้าย ownership จะทำให้ทั้ง instance (ในกรณีนี้นี้ user1) จะไม่ valid ไปเลย
(จริงๆ ทางหนังสือบอกว่าสามารถประกาศ field ของ struct แบบ email: &str
เพื่อรับค่าเป็น reference แทน แต่ปัญหาคือเราต้องกำหนด lifetime ให้กับมัน ซึ่งเค้ายังไม่เฉลย บอกว่าจะบอกให้บทที่ 10)
- Tuple structs ผมคิดว่าคือ Named Tuples
- Unit-Like Structs -> Empty Structs, อันนี้เหมือนจะเอาไปใช้ทีหลังเรื่อง traits
Debug
trait -> ตัวนี้เหมือนเป็นของแถมว่าเราสามารถติด traitDebug
ให้กับ struct ได้เพื่อให้มันสามารถถูก displayed ผ่านprintln!()
ได้
ยกตัวอย่างโค้ดด้านล่างนี้
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {rect1:?}");
}
แบบนี้ก็จะ print rect1 is Rectagle { width: 30, height: 50 }
ออกมาทาง stdout
ให้เลยโดยที่เราไม่ต้องไป implement Display trait เอง
- ของแถมอีกอันที่ผมคิดว่าน่าสนใจมากก็คือ
dbg!
ที่ทำให้เราสามารถ debug แบบ inline ได้เลย แถมไม่ต้องห่วงเรื่อง ownership ด้วยเพราะตัว function คือ ownership ให้เสมอ
ยกตัวอย่างเช่นโค้ดนี้
let rect1 = Rectangle {
width: dbg!(30 * scale),
height: 50,
};
ก็จะ print output ด้านล่างเข้าไปที่ stderr
`[src/main.rs:10:16] 30 * scale = 60`
- อีกหนึ่ง feature ของ Struct ก็คือเราสามารถ associate functions ให้กับมันได้แบบนี้
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
ตัว &self
ก็จะแทน reference ของ instance ที่ call function นี้ โดยถ้า function รับ self
เข้ามาแบบนี้จะเรียกว่า method
(และสามารถรับเป็นทั้ง self
&self
หรือ &mut self
ก็ได้)
ส่วนถ้า associated function ไม่ได้รับ self
เข้ามาก็จะไม่ใช่ method เช่น
impl Rectangle {
fn new(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
ก็จะถูกเรียกได้แบบนี้ Rectangle::new(10)
เป็นต้น
พอมานั่งคิดๆดู เวลาเรา associate functions ให้กับ Struct ได้แบบนี้ concept มันคล้ายกับ Class ใน OOP พอสมควรเลย แต่ผมอาจจะยังอ่านไม่ถึงเรื่องลึกๆ มันคงต้องมีเหตุผลแหล่ะที่เค้าไม่ได้เรียกมันว่า Class
จบละ บทนี้มือเก่าน่าจะอ่านเร็วพอสมควรเพราะไม่ได้ต่างอะไรมากมายกับ concepts ในภาษาอื่นๆ แค่ต้องคอยคิดเรื่อง Ownership กับเรียกรู้ syntax ใหม่นิดหน่อย!