- ผู้เขียนซึ่งใช้ C++ มานานกว่า 20 ปี ได้แนะนำจุดเปลี่ยนที่ทำให้ ค้นพบข้อดีของ Rust อีกครั้ง ผ่านการบรรยายของ Matt Godbolt
- ใน C++ นั้น ความผิดพลาดจากการสับสนชนิดข้อมูล มักไม่ถูกคอมไพเลอร์ตรวจจับได้อย่างเหมาะสม แต่ Rust จะ ป้องกันอย่างเข้มงวดตั้งแต่คอมไพล์ไทม์
- Rust ไม่ได้มีดีแค่ ความปลอดภัยของหน่วยความจำ เท่านั้น แต่ยังมีการออกแบบที่เอื้อต่อ การป้องกันการใช้งาน API ผิดวิธี ด้วย
- โดยเฉพาะในการจัดการอินพุตขณะรันไทม์ Rust จะ บังคับให้จัดการข้อผิดพลาดอย่างชัดเจน เพื่อลดความเสี่ยง
- ท้ายที่สุด นี่เป็นกรณีตัวอย่างที่แสดงให้เห็นว่า การออกแบบภาษาเป็นเครื่องมือทรงพลังในการป้องกันความผิดพลาดของนักพัฒนา ได้
บทนำ
- การบรรยายของ Matt Godbolt ชื่อ "Correct by Construction" ชี้ให้เห็นถึง ปัญหาในการออกแบบ API ของ C++ ซึ่งสอดคล้องกับปรัชญาของ Rust ด้วย
- การบรรยายนี้เป็นสื่อเริ่มต้นที่ดีสำหรับการทำความเข้าใจจุดแข็งของ Rust
What's in a type — ข้อจำกัดของ C++
- ฟังก์ชันซิกเนเจอร์อย่าง
void sendOrder(const char *symbol, bool buy, int quantity, double price) นั้น เสี่ยงต่อความผิดพลาดอย่างมาก
- หากใช้เพียงชนิดข้อมูลพื้นฐานอย่าง
bool, int, double คอมไพเลอร์จะ ไม่เตือนแม้ใส่ชนิดข้อมูลผิด
- type alias อย่าง
using Price = double ไม่ได้มีความสามารถในการแยกชนิดข้อมูลจริง
- เมื่อใช้คลาสและตัวสร้าง
explicit เพื่อสร้าง Quantity, Price คอมไพเลอร์จะช่วยจับข้อผิดพลาดบางส่วนได้ แต่:
- ยังยอมให้ใช้ค่าติดลบได้ ซึ่งจะกลายเป็นปัญหาเฉพาะตอนรันไทม์
- สามารถใช้
static_assert และเทมเพลตเพื่อบังคับ การตรวจสอบตั้งแต่คอมไพล์ไทม์ ได้
- แต่การแปลงค่าขณะรันไทม์อย่าง
atoi ก็ยัง ก่อให้เกิด integer overflow ได้ และคอมไพเลอร์ไม่สามารถตรวจจับได้
Rust ต่างออกไปอย่างไร?
- แม้จะเป็นนิยามฟังก์ชันแบบเดียวกัน Rust ก็สามารถแสดงข้อผิดพลาดเรื่อง ชนิดข้อมูลไม่ตรงกัน ได้อย่างชัดเจนตั้งแต่ขั้นตอนคอมไพล์
- การนิยาม ชนิดข้อมูลใหม่ อย่าง
struct Price(pub f64); struct Quantity(pub u64); ก็ทำได้ง่าย และยัง ป้องกันอินพุตค่าติดลบ ได้อย่างเป็นธรรมชาติ
- การแปลงสตริงขณะรันไทม์อย่าง
"string".parse::<u64>() ก็จำเป็นต้องมี การจัดการข้อผิดพลาดแบบชัดเจน
- หากใช้
.expect() เพื่อ unwrap ค่าแบบบังคับ ก็อาจทำให้โปรแกรมล่มตอนรันไทม์ได้ แต่ยังถูกเน้นว่า ดีกว่าความผิดพลาดเงียบ ๆ ใน C++
สรุป
- Rust ปกป้องนักพัฒนาไม่ใช่แค่ด้วยความเสถียรของหน่วยความจำ แต่ยังผ่าน การป้องกันการใช้ API ผิดวิธี, การตรวจสอบตั้งแต่คอมไพล์ไทม์, และระบบชนิดข้อมูลที่ชัดเจน
- สิ่งนี้แสดงให้เห็นว่า พลังของการออกแบบภาษา สามารถช่วยป้องกันความผิดพลาดของนักพัฒนาได้ตั้งแต่ต้นทาง
- ผู้เริ่มต้น Rust อาจต้องเผชิญกับความยากจาก borrow checker แต่ปัญหานี้จะค่อย ๆ คลี่คลายไปเมื่อเวลาผ่านไป
- แม้ C++ จะพัฒนามาอย่างมากในเชิงประวัติศาสตร์ แต่ก็ยังเห็นได้ชัดว่า ยากที่จะมอบความปลอดภัยและความชัดเจนในระดับพื้นฐานแบบเดียวกับ Rust
อ้างอิง
4 ความคิดเห็น
ดูเหมือนว่าส่วนใหญ่ของสิ่งที่มักถูกยกมาเป็นข้อเสียของ C++ นั้น ส่วนมากเป็นสิ่งที่ยังคงไว้เพราะความเข้ากันได้กับภาษา C
จะสามารถปรับเปลี่ยนให้พัฒนาโดยละทิ้งความเข้ากันได้กับ C ได้ไหม?
คงจะดีกว่านี้ถ้าไม่มี
unsafeให้ใช้เสียเลยภาษาต้นตำรับ = Rust
ความคิดเห็นจาก Hacker News
ข้อดีใหญ่ที่สุดของ Rust คือการใช้
Resulttype ที่ทำให้รูปแบบการส่งต่อข้อผิดพลาดเป็นแบบเดียวกัน จึงไม่ต้องคอยกังวลกับการจัดการ exception หรือรูปแบบการคืนค่าข้อผิดพลาดที่หลากหลาย?และ functional interface ของ Result ทำให้การจัดการข้อผิดพลาดทั้งสนุกและรับมือได้ง่ายมีความไม่พอใจกับ C++ อยู่มาก โดยเฉพาะประเด็นที่ต้องจำกฎจำนวนมาก และถ้าพลาดแม้เพียงข้อเดียว โค้ดก็อาจเปราะบางได้
โค้ด C++ ที่เขียนอยู่ตอนนี้มีลักษณะคล้าย Rust ใช้ type ที่ชัดเจนและแข็งแรง รวมถึงการจัดการอายุการใช้งานที่ชัดเจน
ปัญหา implicit conversion ของ C++ เป็นปัญหาของไลบรารีมากกว่าของตัวภาษา
Rust ไม่มี keyword arguments หรือ tuple ที่มีชื่อ ทำให้การใช้โครงสร้าง Args/Options ไม่ค่อยสะดวก
ตัวเลือก
-Wconversionสามารถจับปัญหาการแปลงค่าบางประเภทได้ แต่ใช้ไม่ได้กับทุกกรณีจุดที่ Rust ดีกว่าคือไม่มีการแปลงตัวเลขแบบ implicit ใน C++ ควรหลีกเลี่ยง
atoiและใช้ฟังก์ชันแปลงค่าของ STL แทนฟีเจอร์ที่คล้ายกับข้อจำกัดของ SQL หรือ custom type และ validator ของ pydantic ยังไม่มีใน Rust หรือ Golang
ถ้าสนใจพอดแคสต์โปรแกรมมิง "Two's Complement" ของ Matt และ Ben Rady ก็ถือว่าน่าลองฟัง