เมื่อ Rust’s Default Trait พบกับปรัชญา Essentialism

Share
เมื่อ Rust’s Default Trait พบกับปรัชญา Essentialism
Photo by Isaac Quesada on Unsplash

ในฐานะโปรแกรมเมอร์ เรามักหมกมุ่นอยู่กับการเปลี่ยนแปลง (Mutation) เราสนใจว่า State จะไหลจาก A ไป B อย่างไร แต่เรามักลืมตั้งคำถามที่สำคัญที่สุดคำถามหนึ่ง:

“ก่อนที่สิ่งนี้จะกลายเป็นสิ่งนั้น… มัน ‘เป็น’ อะไรมาก่อน?”

นี่ไม่ใช่แค่คำถามเชิงตรรกะ แต่มันคือคำถามเชิงปรัชญาที่ลึกซึ้ง และในภาษา Rust คำตอบของคำถามนี้มักจะถูกซ่อนไว้ใน Default trait

สุนทรียศาสตร์ของความว่างเปล่า: นิยามของ ‘แก่นสาร’

ในโลกของปรัชญาตะวันตก มีแนวคิดที่เรียกว่า “Essentialism” ซึ่งเชื่อว่าสรรพสิ่งมี “แก่นสาร” (Essence) ที่ทำให้มันเป็นสิ่งนั้น หากขาดแก่นสารนี้ไป สิ่งนั้นจะสูญเสียอัตลักษณ์ของมันทันที

ลองจินตนาการถึง “เครื่องดื่ม” (Type Drink) แก่นสารของมันคืออะไร? การเป็นของเหลว? การมีรสชาติ? หรือการถูกใช้เพื่อดับกระหาย? ถ้าผมสั่ง "เครื่องดื่ม" จากบาร์เทนเดอร์โดยไม่ระบุเจตจำนง ผมกำลังเรียกหาแก่นสารที่พื้นฐานที่สุดของ Type นั้น

ใน Rust, Default trait ทำหน้าที่แบบเดียวกัน มันคือการประกาศก้องว่า "นี่คือสภาวะพื้นฐานที่สุดที่ฉันดำรงอยู่ได้"

เมื่อคุณเขียนโค้ดนี้:

impl Default for Drink {
fn default() -> Self {
Self {
name: String::from("เหล้าขม"),
cost: 0,
}
}
}

คุณไม่ได้แค่เขียน Function เพื่อคืนค่าเริ่มต้น แต่คุณกำลังนิยามว่า “เครื่องดื่มจำลองในบาร์แห่งนี้มีแก่นแท้คือความขมขื่นที่ราคาถูกที่สุด”

ภัยร้ายของการมีตัวตนโดยบังเอิญ

ความมักง่ายมักนำไปสู่จุดจบที่ไม่สวยงาม นักพัฒนารุ่นใหม่หลายคนเผลอใช้ #[derive(Default)]แปะไว้บนหัว Struct โดยไม่คิดถึงแก่นสารของมัน

#[derive(Default)]
struct User {
id: u64,
name: String,
role: String,
}

ถ้าตาม Default ของ Rust ค่า id จะเป็น 0, name จะเป็น "" (String ว่าง), และ roleก็จะเป็น "" ... นี่คือการสร้าง "มนุษย์ไร้วิญญาณ" ขึ้นมาในระบบของคุณ User คนนี้ดำรงอยู่ได้โดยไม่มี ID ไม่มีชื่อ ไม่มีบทบาท มันคือขยะ (Garbage Data) ที่ถูกต้องตามกฎของ Type System แต่ผิดต่อกฎของความเป็นจริง

Essentialism สอนให้เราแยกแยะระหว่าง “สิ่งที่ติดตัวมาแต่กำเนิด” กับ “สิ่งที่โลกปรุงแต่งทีหลัง” หากสิ่งใดต้องรอ Input จากภายนอก สิ่งนั้นไม่ใช่แก่นสาร

Option และ None: แก่นสารของความไม่แน่นอน

Rust ออกแบบมาอย่างชาญฉลาดในเรื่องนี้ ลองดูที่ Option<T> แก่นสารของมันคืออะไร? ไม่ใช่ค่า Some ที่ห่อหุ้มข้อมูลมหาศาลไว้ แต่มันคือ None ความว่างเปล่าคือจุดเริ่มต้นของมัน

impl Default for Option<T> คืนค่า None เสมอ... นี่คือสภาวะพื้นฐานที่ปลอดภัยที่สุด มันคือการยอมรับว่า "ในจุดเริ่มต้น ฉันคือความไม่แน่นอน"

เช่นเดียวกับ Vec<T> แก่นสารของมันไม่ใช่ List ที่เต็มไปด้วยข้อมูลหมื่นแถว แต่มันคือ Vec::new() คือพื้นที่ว่างที่พร้อมจะรับข้อมูล การกำหนด Default แบบนี้คือการพา Type นั้นกลับไปที่ "จุดศูนย์" (Zero Point) เพื่อยืนยันว่า ต่อให้ไม่มี Input ใดๆ เลย ฉันก็ยังมีที่ยืนในหน่วยความจำ

สู่การออกแบบที่ชัดเจน

ดังนั้น ครั้งต่อไปที่คุณกำลังออกแบบ Domain Model ใน Rust ขอให้หยุดคิดสักนิด:

  1. อย่าปล่อยให้ Machine กำหนดแก่นสารแทนคุณ: จงหลีกเลี่ยงการใช้ #[derive(Default)] สำหรับ Struct ที่มี Logic ซับซ้อน ถ้าคุณต้องการ Default จง impl Default ด้วยมือของคุณเอง (Manual Implementation)
  2. ถามคำถามสุดท้าย: ก่อนที่คุณจะสั่ง ..Default::default() เพื่อเติมเต็มช่องว่างใน Struct ให้ถามตัวเองว่า "ถ้าโลกนี้ล่มสลายเหลือเพียงค่านี้ ค่านี้ยังนิยามสิ่งที่ฉันสร้างได้อยู่ไหม?" หากคำตอบคือไม่ สิ่งนั้นไม่ควรมี Default
  3. เคารพความบริสุทธิ์: ถ้า Type ของคุณไม่มีสภาวะเริ่มต้นที่สมเหตุสมผล ก็อย่าพยายามสร้างมันขึ้นมา เพียงเพื่อให้โค้ดเขียนง่ายขึ้น มันคือการบิดเบือนแก่นสาร และนั่นคือจุดเริ่มต้นของ Bug ที่แก้ยากที่สุด

ในโลกที่ซับซ้อนและเต็มไปด้วยความผิดพลาด… การมี “แก่นสาร” ที่ชัดเจนและปลอดภัย คือสิ่งเดียวที่ช่วยให้สถาปัตยกรรมของคุณรอดพ้นจากความโกลาหล

ขอให้ 0 ของคุณ คือศูนย์ที่มีความหมาย

เกี่ยวกับผู้เขียน: Uncle Quin — Software Engineer ผู้หลงใหลในการเชื่อมโยงวิศวกรรมซอฟต์แวร์เข้ากับปรัชญา


หากบทความนี้มีประโยชน์

คุณสามารถติดตาม Late Night with Uncle Quin ได้ทาง

ที่ที่เราคุยกันเรื่อง software, engineering mindset และอนาคตของ developer

แบบไม่ต้องใส่สูท

แต่ใส่ความจริงของวงการเข้าไปเต็ม ๆ

Read more

The Five Ideals

The Five Ideals

จากหนังสือ The Unicorn Project ในนั้นจะมีที่ปรึกษาเก่ง ๆ คนหนึ่งชื่อ Erik เป็นเหมือนเซ็นเซหาตัวยาก เดี๋ยวก็มา เดี๋ยวก็ไป ถ้าโชคดีก็จะเจอเค้าว่าง แล้วเค้าก็จะสอนอะไรที่สำคัญ ๆ แต่ฟังดูล้ำ ๆ และลึกซึ้ง

By Chokchai Phatharamalai
Communication, Reliability and Modern concepts - System Design, Simply Explained

Communication, Reliability and Modern concepts - System Design, Simply Explained

บทความนี้จะอธิบายพื้นฐานของ System Design โดยต่อยอดจากตัวอย่าง Gym Application เพื่อให้เห็นภาพว่า เมื่อระบบเริ่มเติบโตและถูกแยกออกเป็นหลาย Service แต่ละ Service จะสื่อสารและทำงานร่วมกันอย่างไร งานแบบไหนควรสื่อสารแบบ Synchronous งานแบบไหนควรประมวลผลแบบ Asynchronous รวมถึงอธิบาย Concept พื้นฐานต่าง ๆ ที่ควรรู้ เช่น API

By Boonsong Srithong
Race condition

Race condition

ผมกำลังอ่านหนังสือ The Unicorn Project ซึ่งเป็นนิยายเกี่ยวกับ developer สาวคนหนึ่งที่ใช้ชีวิตในองค์กรท่ามกลางกระแส digital disruption อ่านแล้วสนุกมาก เป็นเรื่องที่ตื่นเต้น และหลาย ๆ dynamic ที่เกิดขึ้นก็คล้ายกับที่ผมพบเจอในบริษัทซอฟต์แวร์ เลยทำให้

By Chokchai Phatharamalai
Distributed Systems Concepts - System Design, Simply Explained

Distributed Systems Concepts - System Design, Simply Explained

ในช่วงแรก Product เราสามารถออกแบบระบบให้อยู่ใน Server และ Database เดียวกันได้ แต่เมื่อระบบโตขึ้น มีผู้ใช้งานเพิ่มขึ้น ข้อมูลเยอะขึ้น ระบบเริ่มทำงานช้าลดเรื่อยๆ สิ่งที่หนีไม่ได้คือการ Decomposition Service ไปเป็น Distributed System เช่น Member Service, Booking Service, Payment

By Boonsong Srithong