นักพัฒนา Rust ถกเถียงการเลิกใช้วิธีจัดการข้อผิดพลาดแบบดั้งเดิมด้วย Function-Specific Error Types

ทีมชุมชน BigGo
นักพัฒนา Rust ถกเถียงการเลิกใช้วิธีจัดการข้อผิดพลาดแบบดั้งเดิมด้วย Function-Specific Error Types

ชุมชนนักพัฒนา Rust กำลังถกเถียงกันอย่างกระตือรือร้นเกี่ยวกับการละทิ้งมาตรฐานปัจจุบันที่ใช้ error enums แบบกว้างๆ เพื่อหันไปใช้แนวทางการจัดการข้อผิดพลาดที่เฉพาะเจาะจงและแม่นยำกว่า การถกเถียงนี้ได้รับแรงผลักดันเมื่อนักพัฒนาต้องการหาวิธีที่ดีกว่าในการแสดงความต้องการด้านข้อผิดพลาดผ่านระบบ type ของ Rust

ปัญหาปัจจุบันของ Broad Error Enums

โปรเจกต์ Rust ส่วนใหญ่ในปัจจุบันใช้รูปแบบการสร้าง error enum ขนาดใหญ่อันหนึ่งต่อ module หรือ crate ที่ครอบคลุมกรณีข้อผิดพลาดที่เป็นไปได้ทั้งหมด แม้ว่าแนวทางนี้จะช่วยลด boilerplate code แต่ก็สร้างปัญหาสำคัญ คือ functions ส่งคืน error types ที่รวม variants ซึ่งมันไม่สามารถสร้างขึ้นจริงได้ สิ่งนี้บังคับให้นักพัฒนาต้องกำหนดด้วยตนเองว่า error variants ใดที่เกี่ยวข้องกับการเรียก function แต่ละครั้ง โดยมักต้องพึ่งพาเอกสารที่อาจไม่สมบูรณ์หรือล้าสมัย

ชุมชนตระหนักว่านี่เป็นปัญหาพื้นฐานที่บ่อนทำลายจุดแข็งหลักอย่างหนึ่งของ Rust - การใช้ระบบ type เพื่อป้องกันข้อผิดพลาดในการเขียนโปรแกรม เมื่อ error types กว้างเกินไป compiler จะไม่สามารถช่วยนักพัฒนาเข้าใจสิ่งที่อาจผิดพลาดได้จริงในบริบทเฉพาะของพวกเขา

Function-Specific Error Types ได้รับการสนับสนุน

นักพัฒนาจำนวนมากขึ้นเรื่อยๆ สนับสนุนการสร้าง error types แยกสำหรับแต่ละ function หรือ action แนวทางนี้ช่วยให้แน่ใจว่าทุก function จะส่งคืนเพียงข้อผิดพลาดที่สามารถสร้างขึ้นได้จริงเท่านั้น ทำให้โค้ดแม่นยำและอธิบายตัวเองได้มากขึ้น compiler จึงสามารถให้คำแนะนำที่ดีกว่าเกี่ยวกับข้อผิดพลาดใดที่ต้องจัดการในแต่ละสถานการณ์

อย่างไรก็ตาม ความแม่นยำนี้มาพร้อมกับต้นทุน การเพิ่ม error variants ใหม่ต้องอัปเดต call chain ทั้งหมด ซึ่งอาจน่าเบื่อระหว่างการพัฒนาอย่างต่อเนื่อง นักพัฒนาบางคนเห็นว่าการแลกเปลี่ยนนี้คุ้มค่า เพราะ compiler แสดงอย่างชัดเจนว่าการจัดการข้อผิดพลาดต้องการความสนใจตรงไหน ป้องกันกรณีขอบที่ถูกลืม

การเปรียบเทียบแนวทางการจัดการข้อผิดพลาด:

แนวทาง ข้อดี ข้อเสีย
Module-wide error enums โค้ดซ้ำซ้อนน้อย ใช้งานง่าย ฟังก์ชันส่งคืนตัวแปรข้อผิดพลาดที่ไม่เกี่ยวข้อง
Function-specific errors ประเภทข้อผิดพลาดที่แม่นยำ เอกสารประกอบดีกว่า ต้องอัปเดตห่วงโซ่การเรียกใช้ทั้งหมด
Macro-generated error sets การแปลงอัตโนมัติ การจัดองค์ประกอบทางคณิตศาสตร์ การดีบักซับซ้อน การสร้างโค้ด "มหัศจรรย์"
Third-party solutions ระบบนิเวศที่หลากหลาย โซลูชันที่ได้รับการพิสูจน์แล้ว การพึ่งพาเพิ่มเติม การแยกส่วน

โซลูชันแบบ Macro-Based เผชิญกับการต่อต้าน

crates หลายตัวเช่น error-set และ terrors พยายามแก้ปัญหาเหล่านี้โดยใช้ macros เพื่อสร้าง error types ที่แม่นยำโดยอัตโนมัติ เครื่องมือเหล่านี้ช่วยให้นักพัฒนากำหนด error sets ที่รวม variants เฉพาะเจาะจงกับ unions ของ error sets อื่นๆ สร้างแนวทางทางคณิตศาสตร์มากขึ้นในการประกอบ error

แม้จะมีความสามารถทางเทคนิค แต่โซลูชันแบบ macro-based เหล่านี้เผชิญกับการต่อต้านอย่างมากจากชุมชน นักพัฒนาหลายคนแสดงความเหนื่อยล้ากับ macro และชอบแนวทางที่ตรงไปตรงมาซึ่งไม่พึ่งพา code generation magic ความกังวลคือการใช้ macro มากเกินไปทำให้ codebase เข้าใจและ debug ยากขึ้น คล้ายกับปัญหาที่เห็นใน dynamic languages

ผมเริ่มเหนื่อยล้ากับ macro ใน Rust แล้ว ในความเห็นของผม ยิ่งใช้ magic น้อยใน codebase ยิ่งดี

ไลบรารี Rust ยอดนิยมสำหรับการจัดการข้อผิดพลาด:

  • anyhow - การจัดการข้อผิดพลาดแบบอเนกประสงค์สำหรับแอปพลิเคชัน
  • thiserror - แมโครสำหรับสร้างประเภทข้อผิดพลาดแบบกำหนดเอง
  • error-set - การจัดเรียงชุดข้อผิดพลาดแบบใช้แมโคร
  • terrors - การจัดการชุดข้อผิดพลาดในระดับประเภท
  • SmartErr - กระบวนทัศน์การจัดการข้อผิดพลาดแบบทางเลือก

ข้อจำกัดของ Standard Library ผลักดันการพึ่งพา Third-Party Dependencies

การถกเถียงนี้เน้นย้ำปัญหาที่กว้างขึ้นของแนวทางการพัฒนา standard library ของ Rust ไม่เหมือนภาษาที่รวมการจัดการข้อผิดพลาดและการสนับสนุน async runtime อย่างครอบคลุม Rust ต้องการให้โปรเจกต์ส่วนใหญ่พึ่งพา third-party crates เช่น anyhow, thiserror, และ tokio สำหรับฟังก์ชันพื้นฐาน

แม้ว่าแนวทางนี้จะช่วยให้เกิดนวัตกรรมและการทดลองกับโซลูชันต่างๆ ในระบบนิเวศ แต่ก็หมายความว่าทุกโปรเจกต์ Rust ต้องตัดสินใจเชิงสถาปัตยกรรมพื้นฐานเกี่ยวกับการจัดการข้อผิดพลาดก่อนเขียนโค้ดจำนวนมาก นักพัฒนาบางคนกังวลเกี่ยวกับ dependency bloat โดยเฉพาะเมื่อโปรเจกต์ง่ายๆ ต้อง build crates หลายสิบตัวสำหรับฟังก์ชันพื้นฐาน

มองไปข้างหน้า

การถกเถียงที่กำลังดำเนินอยู่สะท้อนความท้าทายที่กว้างขึ้นของ Rust ในการสร้างสมดุลระหว่าง type safety กับการใช้งานจริงที่ปฏิบัติได้ แม้ว่าชุมชนจะเห็นพ้องกันว่าแนวทางการจัดการข้อผิดพลาดปัจจุบันมีข้อบกพร่องสำคัญ แต่ก็ไม่มีฉันทามติเกี่ยวกับเส้นทางที่ดีที่สุดไปข้างหน้า นักพัฒนาบางคนยังคงผลักดันให้มี error types ที่เฉพาะเจาะจงและแม่นยำกว่าแม้จะต้องทำงานเพิ่มเติม ในขณะที่คนอื่นๆ ชอบแนวทางที่ง่ายกว่าโดยใช้เครื่องมือที่มีอยู่เช่น anyhow สำหรับ application code

การถกเถียงนี้ยังเผยให้เห็นคำถามที่ลึกซึ้งกว่าเกี่ยวกับว่าแนวทางพื้นฐานของ Rust ในการจัดการข้อผิดพลาดผ่าน Result types เป็นตัวเลือกที่ถูกต้องหรือไม่ โดยบางคนแนะนำว่าระบบแบบ exception-based ที่มีข้อมูลบริบทที่หลากหลายอาจจะดีกว่าสำหรับการพัฒนาระยะยาวของภาษา

อ้างอิง: On Error Handling in Rust