6 คะแนน โดย GN⁺ 2025-09-27 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
  • ในด้านการจัดการหน่วยความจำ Zig มอบแนวทางที่เรียบง่ายและตรงไปตรงมากว่า Rust
  • borrow checker ของ Rust แม้จะทรงพลัง แต่สำหรับการพัฒนาเครื่องมือ CLI ขนาดเล็กกลับสร้างความซับซ้อนเกินจำเป็นและเพิ่มภาระให้ผู้พัฒนา
  • การจัดการหน่วยความจำแบบแมนนวลของ Zig ทำให้สามารถบรรลุความปลอดภัยของหน่วยความจำได้อย่างมีประสิทธิภาพ ด้วยเครื่องมือที่เหมาะสมและวินัยของนักพัฒนาเพียงเล็กน้อย
  • ความปลอดภัยของโปรแกรมไม่ได้มีแค่ความปลอดภัยของหน่วยความจำเท่านั้น แต่ยังรวมถึงพฤติกรรมที่คาดเดาได้ ประสิทธิภาพที่ควบคุมได้ และการปกป้องข้อมูลด้วย
  • Rust เหมาะกับระบบขนาดใหญ่ แต่สำหรับเครื่องมือ CLI ขนาดเล็กที่เน้นการใช้งานจริง Zig ได้เปรียบกว่าในด้านประสิทธิภาพการพัฒนาและการดูแลรักษา

ภาพรวม

ช่วงหลังมานี้เวลาสร้างเครื่องมือ CLI มักเลือก Zig ก่อน Rust

พื้นฐานของการจัดการหน่วยความจำ: สแตกและฮีป

  • สแตกเป็นพื้นที่หน่วยความจำแบบขนาดคงที่ที่รวดเร็ว ใช้เก็บข้อมูลชั่วคราวมาก เช่น พารามิเตอร์ของฟังก์ชัน ตัวแปรภายใน และที่อยู่สำหรับการคืนค่า
  • ฮีปเป็นพื้นที่สำหรับการจัดสรรหน่วยความจำแบบไดนามิก ใช้เมื่อข้อมูลมีอายุยาวขึ้นหรือมีขนาดที่ตัดสินได้ตอนรันไทม์
  • สแตกมีโครงสร้างเรียบง่าย แต่พื้นที่จำกัด ส่วนฮีปมีเรื่องความเร็วและการกระจัดกระจายของหน่วยความจำที่ต้องใส่ใจ

Borrow Checker ของ Rust

  • borrow checker ของ Rust รับประกันความปลอดภัยของหน่วยความจำในขั้นคอมไพล์
  • มันบังคับใช้กฎอย่างการอ้างอิง ความเป็นเจ้าของ และ lifetime เพื่อป้องกันข้อผิดพลาดอย่างการ dereference null pointer หรือ dangling pointer ตั้งแต่ก่อนรันจริง
  • อย่างไรก็ตาม การตรวจสอบความปลอดภัยของหน่วยความจำนี้เกิดขึ้นเฉพาะในระดับคอมไพล์ และไม่ได้ช่วยขจัดความผิดพลาดของผู้ใช้หรือปัญหาการออกแบบ ownership ที่ซับซ้อนออกไปได้ทั้งหมด

กรณีศึกษา: Notes CLI ของฉันเอง

  • ตอนพยายามเขียน CLI สำหรับจัดการโน้ตส่วนตัวด้วย Rust ต้องฝืนออกแบบโครงสร้างใหม่เพราะborrow checker
  • ในทางกลับกัน Zig สามารถใช้แค่ allocator เพื่อสร้างดัชนีแบบอิง pointer และแก้ไขหรือลบข้อมูลได้อย่างอิสระและเรียบง่ายกว่ามาก
  • borrow checker ของ Rust มีเป้าหมายที่ชัดเจน แต่ Zig ก็สามารถบรรลุประสิทธิภาพและความปลอดภัยในระดับสูงได้เช่นกัน ด้วยเพียงความรู้พื้นฐานเรื่องการจัดการหน่วยความจำและวินัยในการพัฒนา

ความปลอดภัยของเครื่องมือ CLI ไม่ได้มีแค่ความปลอดภัยของหน่วยความจำ

  • ความปลอดภัยที่แท้จริงของโปรดักต์ครอบคลุมองค์ประกอบหลากหลาย เช่น พฤติกรรมที่คาดเดาได้ การตอบกลับที่มีความหมายเมื่อเกิดข้อผิดพลาด การปกป้องข้อมูลอ่อนไหว และความทนทานต่อการโจมตี
  • ไม่ว่าจะเป็น Rust หรือ Zig ถ้าไม่ตอบโจทย์เงื่อนไขนอกเหนือจากความปลอดภัยของหน่วยความจำ ก็ยังไม่อาจเรียกว่า "ปลอดภัย" ได้
  • ตัวอย่างเช่น ถ้า CLI เขียนทับข้อมูลแบบเงียบ ๆ เมื่อเกิดข้อผิดพลาด หรือกำหนดสิทธิ์ไฟล์ผิด ผู้ใช้อาจเจอปัญหาร้ายแรงได้
  • ความปลอดภัยของเครื่องมือ CLI

    • พฤติกรรมที่คาดเดาได้: ต้องรับประกันการทำงานที่สม่ำเสมอและชัดเจนแม้ได้รับอินพุตที่ผิดหรือเจอสถานการณ์ไม่คาดคิด
    • การป้องกันการล่มและข้อมูลเสียหาย: ต้องจัดการข้อผิดพลาดอย่างเหมาะสม และป้องกันข้อมูลเสียหายหรือการล่มที่ไม่ถูกแจ้ง
    • การจัดการประสิทธิภาพ: แม้ประมวลผลข้อมูลจำนวนมากก็ไม่ควรใช้ทรัพยากรเกินจำเป็นหรือทำให้การตอบสนองลดลง
    • การปกป้องข้อมูลอ่อนไหว: ต้องระวังไฟล์ชั่วคราวและการตั้งค่าสิทธิ์
    • ความทนทานต่อการโจมตี: ต้องแข็งแรงต่อการตรวจสอบอินพุต, memory overflow, การโจมตีแบบ injection เป็นต้น

จุดแข็งและข้อจำกัดของ Rust Borrow Checker

  • จุดแข็ง

    • ป้องกัน data race และการอ้างอิงซ้ำซ้อน: คอมไพเลอร์รับประกันกติกาเรื่อง mutable reference เดี่ยวและ immutable reference หลายตัว
    • การรับประกันอันแข็งแกร่งในขั้นคอมไพล์: บั๊กด้านหน่วยความจำส่วนใหญ่ถูกป้องกันก่อนรันโปรแกรม
    • ค้นพบบั๊กได้ตั้งแต่ระยะแรก: เป็นข้อได้เปรียบมากในบริการเชิงพาณิชย์หรือระบบที่มี concurrency
  • ข้อจำกัดและความไม่สะดวก

    • ภาระทางความคิด: แม้เป็นงาน CLI เล็ก ๆ ก็ยังต้องคอยคิดเรื่อง ownership/lifetime/reference อยู่เสมอ
    • boilerplate/ความบิดเบี้ยวของโครงสร้าง: wrapper อย่าง Rc, RefCell, การใช้ clone มากเกินไป หรือการออกแบบโครงสร้างใหม่ ทำให้โฟกัสกลายเป็นการ "ทำให้คอมไพเลอร์พอใจ" แทนที่จะเป็นการ "แก้ปัญหา"
    • รับมือกับบั๊กเชิงตรรกะหรือสถานะไม่ได้: มันรับประกันแค่กฎด้านหน่วยความจำ แต่ไม่ได้รับประกันความคาดเดาได้ ความผิดพลาดเชิงตรรกะ หรือความสมบูรณ์ของข้อมูล
    • ความซับซ้อนใน edge case: ในกรณีอย่างแคช สถานะโกลบอล หรือ mutable index มักเกิดการชนกันของ lifetime ได้ง่าย
  • ผลลัพธ์คือ ในโปรเจกต์ CLI ขนาดเล็ก borrow checker ของ Rust กลายเป็น "ภาษีทางจิตใจ" สำหรับนักพัฒนา และอาจทำให้ระบบซับซ้อนเกินความจำเป็นจริง

แนวทางของ Zig ต่อความปลอดภัยและความเรียบง่าย

  • Zig ใช้พื้นฐานเป็นการตรวจสอบความปลอดภัยแบบเลือกได้และการจัดการหน่วยความจำแบบแมนนวล
  • ภาษานี้มีแนวคิดเรื่อง allocator มาในตัว ทำให้สามารถออกแบบการใช้หน่วยความจำได้อย่างมีโครงสร้างและคาดเดาได้
  • ยังสามารถสร้าง custom allocator เพื่อกำหนดรูปแบบการจัดการหน่วยความจำให้เหมาะกับลักษณะของโปรเจกต์ได้ด้วย
  • ด้วยไวยากรณ์ defer ของ Zig การปล่อยทรัพยากรอัตโนมัติและการทำความสะอาดทรัพยากรเมื่อออกจากสโคปจึงตรงไปตรงมาอย่างมาก
  • ต่างจาก Rust ที่เน้นข้อบังคับอย่างหนัก Zig เน้นความรับผิดชอบของนักพัฒนามากกว่า จึงต้องมีวินัย แต่ถ้าออกแบบโครงสร้างให้ดี ก็ทำให้บรรลุและรักษาความปลอดภัยของหน่วยความจำได้ง่าย
  • Zig มีโค้ดที่กระชับกว่า และการเปลี่ยนโครงสร้างอย่าง pointer, list, index ต่าง ๆ ก็ง่ายกว่า Rust มาก
  • สามารถเขียนโค้ดที่ปลอดภัยและมีประสิทธิภาพในระดับเดียวกันได้ โดยไม่ต้องถูกผูกมัดแบบ Rust
  • นอกจากนี้ ฟีเจอร์ comptime ของ Zig ยังช่วยมากในด้านการรันโค้ด การทดสอบ และการปรับแต่งประสิทธิภาพในขั้นคอมไพล์

ความสำคัญของประสบการณ์นักพัฒนา (Developer Ergonomics)

  • ประสบการณ์นักพัฒนา (ergonomics) ครอบคลุมตั้งแต่ไวยากรณ์ของภาษา เครื่องมือ เอกสาร ไปจนถึงชุมชน
  • Rust แม้จะรับประกันความปลอดภัยของหน่วยความจำได้อย่างยอดเยี่ยมด้วยกฎอันเข้มงวด แต่กฎและ ceremony ที่มากเกินไปก็ลดทอนประสิทธิภาพการทำงาน
  • Zig เน้นการออกแบบที่ขับเคลื่อนโดยผู้พัฒนา ทำให้เขียน แก้ไข และทำความเข้าใจโค้ดได้ง่ายและรวดเร็วกว่า
  • Zig ช่วยให้นักพัฒนามุ่งกับการแก้ปัญหาแทนการต่อสู้กับเครื่องมือ ด้วยโค้ดที่เข้าใจง่าย การวนรอบพัฒนาเร็ว และภาระทางความคิดที่ต่ำ
  • Zig ไว้วางใจนักพัฒนาและมอบเครื่องมือกับทางเลือกที่เหมาะสม ขณะที่ Rust อาจให้ความรู้สึกว่าควบคุมเข้มงวดและจำกัดมากเกินไป
  • แทนที่จะ "ปกป้องนักพัฒนาจากความผิดพลาด" การเปิดโอกาสให้เรียนรู้และพัฒนาผ่านความผิดพลาดของตนเองต่างหากที่เป็นสภาพแวดล้อมที่เป็นมิตรต่อนักพัฒนา

บทสรุป

  • ในงานอย่างระบบขนาดใหญ่ แบบมัลติเธรด หรือทำงานระยะยาวซึ่งเป็นจุดที่ข้อดีของ Rust โดดเด่นที่สุดนั้น Rust ยังเป็นตัวเลือกที่ยอดเยี่ยมที่สุด
  • แต่สำหรับเครื่องมือ CLI ขนาดเล็กที่เน้นการใช้งานจริง ความเบา ความเรียบง่าย และการพัฒนา/ดูแลรักษาที่รวดเร็วของ Zig เหมาะสมกว่า
  • ความปลอดภัยของหน่วยความจำเป็นเพียงส่วนหนึ่งของจิ๊กซอว์เรื่องความปลอดภัยเท่านั้น ส่วนองค์ประกอบสำคัญต่อเครื่องมือ CLI อย่างพฤติกรรมที่คาดเดาได้ การบำรุงรักษาง่าย และความทนทานนั้น Zig ทำได้ง่ายกว่า
  • ท้ายที่สุด สิ่งสำคัญไม่ใช่การหา "ภาษาที่ดีกว่า" แต่คือการเลือก "เครื่องมือ" ที่เหมาะกับ workflow และลักษณะของโปรเจกต์ของเรา
  • Zig คือภาษาที่ลงตัวอย่างยิ่งสำหรับการพัฒนาเครื่องมือขนาดเล็ก เพราะผสาน "ความปลอดภัยของหน่วยความจำ + ต้นทุนทางความคิดต่ำ + ความเป็นมิตรต่อผู้พัฒนา/ประสิทธิภาพการทำงาน" เข้าไว้ด้วยกัน

3 ความคิดเห็น

 
shakespeares 2025-10-05

ดูเหมือนว่าระบบนิเวศของมันยังไม่เสถียรเท่า Rust นะครับ

 
bus710 2025-09-27

เนื่องจาก Zig มีการเปลี่ยนแปลงที่ทำให้เข้ากันไม่ได้บ่อยมากในเวอร์ชันใหม่ ๆ ... ต่อให้เป็นโปรเจ็กต์เล็กก็คงต้องพยายามผูก ci ไว้แล้วคอยดูแลต่อเนื่องกันไปครับ

 
GN⁺ 2025-09-27
ความเห็นจาก Hacker News
  • ข้อดีของ Zig คือมันทำให้ยังคิดแบบนักพัฒนา C ต่อไปได้ แต่ในระดับหนึ่งก็คิดว่าเป็นเรื่องของความคุ้นเคยล้วน ๆ

    • นักพัฒนาที่คุ้นกับ Rust มากพอแล้วจะไม่ต้องสู้กับ borrow checker อีก เพราะจะคิดเป็นโครงสร้างโค้ดแบบนั้นไปแล้ว

    • แนวทางแบบ “object soup” ใน Rust ใช้ได้ไม่ค่อยดี แต่ก็ไม่ได้คิดว่านั่นเป็นวิธีที่ง่ายกว่าโดยพื้นฐาน เพียงแค่เราคุ้นเลยรู้สึกว่ามันง่าย

    • ถ้ายอมรับสมมติฐานว่า ergonomics (ประสบการณ์การใช้งาน) วัดหรือทำให้เป็นตัวเลขได้ยาก การถกเถียงแบบนี้ก็จะกำกวมต่อไป

      • คล้ายกับการพูดว่า “เก้าอี้ตัวนี้ไม่พังแน่ นั่งได้อย่างสบายใจ แต่อาจจะนั่งไม่ค่อยสบายและหนักกว่าเล็กน้อย แต่คนส่วนใหญ่จะชินจนไม่รู้สึกไม่สบายในไม่ช้า”
      • อย่างประโยคในบทความต้นฉบับที่ว่า “Rust ให้ memory safety แลกกับประสบการณ์นักพัฒนาที่ไม่สะดวกนัก ส่วน Zig ให้ประสบการณ์นักพัฒนาที่ดีกว่าและยังให้ memory safety ได้ด้วยความระมัดระวังเพิ่มอีกเล็กน้อย” สุดท้ายก็คือการแลกระหว่างความปลอดภัยกับการใช้งาน
      • คิดว่าชุมชน Rust ควรยอมรับ trade-off นี้อย่างตรงไปตรงมา เพราะการทำให้ปลอดภัยขึ้นย่อมมาพร้อมความสะดวกที่ลดลงเสมอ
      • นี่เป็นพื้นฐานที่มีอยู่ทั้งในเรื่องความมั่นคงปลอดภัย ชีวิตประจำวัน และซอฟต์แวร์โดยรวม และส่วนใหญ่ก็มักเป็นข้ออ้างที่คลุมเครือหรืออิงความรู้สึก
      • บางคนก็ละเลยประเด็นเรื่อง ergonomics ได้ง่าย ๆ ด้วยการบอกว่า “ถ้าคุ้นแล้วก็ไม่มีปัญหา” ซึ่งอาจฟังเหมือน “ถ้าแค่นี้ยังยาก แปลว่าคุณฉลาดไม่พอ”
    • เรื่อง “การสู้กับ borrow checker” มีที่มาจากยุคที่เข้าใจแค่ lexical lifetime ใน Rust

      • ตอนที่ฉันเรียน Rust ในปี 2021 มันก็เป็นเรื่องเก่าไปแล้ว
      • ในความเป็นจริง คนที่เคยใช้แค่ Python, C, JavaScript ฯลฯ อาจปรับตัวกับ Rust ได้ไม่ง่าย
      • สำหรับฉันมันรับได้ไม่ยาก แต่ดูเหมือนคนส่วนใหญ่จะไม่ได้รู้สึกแบบนั้น
      • แต่ประโยคว่า “ใน Rust คุณแค่ต้องอ่านข้อความวินิจฉัยแล้วแก้โค้ดตามนั้น” ฟังดูเท่น้อยกว่า “ต่อสู้อย่างกล้าหาญกับ borrow checker” มาก และคิดว่าเราควรอธิบายสภาพจริงให้กล้าหาญกว่านี้
    • จากประสบการณ์ของฉัน นักพัฒนา Rust ที่ชำนาญมักโปรย Arc ไว้ทั่ว ๆ เพื่อใช้มันคล้าย garbage collection แบบอัตโนมัติ

      • การจัดการหน่วยความจำแบบ static นั้นเข้มงวดพอสมควร จึงใช้งานกับโครงสร้างข้อมูลซับซ้อนได้ยากในทางปฏิบัติ
      • เพราะเส้นทางที่ต้องไล่ตามเนื่องจาก lifetimes มันเกินขีดความสามารถในการรับรู้ของมนุษย์ไปมาก
    • ฉันก็เห็นโปรเจกต์ Rust โอเพนซอร์สมากมายที่แม้แต่นักพัฒนาชำนาญก็ยังใช้ Arc, Clone, Copy ฯลฯ กระจายอยู่เต็มไปหมด

    • ข้อดีของ Zig คือยังพัฒนาแบบคุ้นมือเหมือน C ได้ แต่ภาษากับ tooling ก็มีฟีเจอร์ช่วยเรื่องความปลอดภัย

      • ตัวอย่างเช่น optional ของ Zig ช่วยหลีกเลี่ยงปัญหา nil dereference ได้
      • ตอนดีบักหรือทดสอบก็ส่ง debug/custom allocator เข้าไปเองได้ จึงตรวจ runtime checks, การเข้าถึงหน่วยความจำ, และ resource leak ได้ง่าย
      • แม้จะไม่ชอบที่ไม่มี interface/trait แบบชัดเจน แต่ก็คิดว่ามันนำไปใช้ได้ง่ายและใช้งานจริงดี
  • ฉันไม่เห็นด้วยกับเนื้อหาต้นฉบับเกือบทั้งหมด

    • Rust ก็ทำให้ต้องคิดเรื่อง lifetime, ownership, และ borrow scope เหมือน C หรือ Zig ความต่างคือมีหรือไม่มีตัวช่วยจากคอมไพเลอร์

    • ต่อให้ฉลาดแค่ไหน มนุษย์ก็พลาดได้เวลาเหนื่อยหรือลังเล การยอมรับว่าตัวเองพลาดได้ต่างหากคือความฉลาด

    • พื้นที่ของโปรแกรมที่คอมไพเลอร์ Rust ถือว่าปลอดภัยนั้นกว้างไม่พอ จึงปฏิเสธโปรแกรมที่ปกติใช้ได้อยู่บ่อยพอสมควร

    • ตัวอย่าง: ถ้าใน struct Foo มี bar กับ baz เป็นสตริงคนละตัว แล้วพยายามทำ mutable reference กับ bar และ immutable reference กับ baz ก็จะคอมไพล์ไม่ผ่าน ทำให้ในสถานการณ์แบบนี้ต้องบิดโครงสร้างโค้ดเพื่อเลี่ยงปัญหา

    • ถ้าจะโต้แย้งกลับ ภาระที่ต้องเปลี่ยนโค้ดไปเป็นดีไซน์ที่ดีรองหรือดีอันดับสามเพียงเพื่อ “หลีกเลี่ยงกรณีที่จริง ๆ แล้วถูกต้องแต่คอมไพเลอร์ปฏิเสธ” นั้นหนักมาก

      • แบบนี้อาจทำให้ดีไซน์ของทั้ง codebase เปลี่ยนไปเลยก็ได้
      • นี่ถูกใช้เป็นคำอธิบายว่าทำไมนักพัฒนาเกมหรือคนที่ทำงานยาก ๆ ถึงรู้สึกว่าการรับ Rust มาใช้เป็นภาระ
      • ถ้า Rust compiler ทำงานได้อย่างแม่นยำโดยไม่มี false positive เลย มันก็คงยอดเยี่ยมมากในแง่ ergonomics เช่นกัน แต่แน่นอนว่าโลกจริงไม่ง่ายแบบนั้น
    • ตัวอย่างข้างบนดูเป็นกรณีศึกษาที่ยอดเยี่ยมมาก อยากถามว่านำไปพูดถึงในบล็อกหรือบทความของฉันได้ไหม

    • พอดูโค้ดข้างบนแล้วกลับยิ่งไม่มั่นใจกับข้ออ้างนั้นมากขึ้น

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

    • พวกเราหลายคนก็เติบโตมากับการสนุกกับซอฟต์แวร์ Unsafe มากมาย เช่น Star Fox 64, MS Paint, FruityLoops

    • เคยอ่านมาว่า Andrew Kelley ผู้สร้าง Zig ทำ Zig ขึ้นมาเพราะไม่มีสภาพแวดล้อมพัฒนาสำหรับซอฟต์แวร์ทำเพลง (DAW) และคิดว่า Zig เหมาะกับซอฟต์แวร์เชิงสร้างสรรค์แบบนั้นมาก

    • ถ้าใครกังวลเรื่อง memory bug ก็ใช้ Rust ไปได้เลย

    • ฉันเชื่อว่า Super Mario World ก็สนุกขึ้นเพราะ memory bug ด้วยเหมือนกัน

    • “ความปลอดภัย” คือคำย่อของ “โปรแกรมของฉันทำงานตามที่ตั้งใจไว้”

      • โค้ดที่ไร้ตรรกะโดยไม่ตั้งใจ (semantic gibberish) ย่อมไม่เป็นผลดีต่อการบรรลุเป้าหมาย
      • แน่นอนว่าบางคนก็เขียนโค้ดให้อ่านยากเชิงศิลปะโดยตั้งใจได้ เช่น IOCCC, การแฮ็ก, หรือ code poetry แต่กรณีแบบนั้นก็ต้อง craft อย่างระมัดระวัง
      • ใน Rust เองก็ทำสิ่งแบบนั้นได้โดยตั้งใจผ่าน escape hatch (unsafe)
      • ข้ออ้างในบทความนี้คือการเขียนโค้ดที่กลายเป็น gibberish โดยไม่ได้ตั้งใจนั้นเป็นข้อดี ซึ่งเป็นสิ่งที่เห็นด้วยได้ยากมาก
      • ถ้าทำให้โค้ดทั้งหมดปลอดภัยได้โดยไม่มีข้อเสียเลย ใครจะไม่อยากได้ล่ะ?
      • การสปีดรัน Super Mario World ฯลฯ ก็ทำได้ด้วย binary patch เช่นกัน ไม่ได้คิดว่าการบิดหน่วยความจำผ่าน input เป็นความสนุกเพียงอย่างเดียว
    • ฉันสับสนเล็กน้อย เลยสงสัยว่าเหตุผลที่คิดว่าความเห็นของฉันแย่คือคุณหมายความว่า memory safety ไม่สำคัญหรือเปล่า

  • รู้สึกเสียดายที่คุณค่าของ borrow checker ถูกประเมินต่ำเกินไป

    • borrow checker ของ Rust รับประกันการเข้าถึงหน่วยความจำที่ถูกต้องไม่ได้ตั้งแต่ตอนคอมไพล์

    • แน่นอนว่ามันก็มาพร้อมความไม่สะดวกที่ต้องเปลี่ยนโครงสร้างโค้ดให้สอดคล้องกับกฎของคอมไพเลอร์

    • ตอนที่ฉันใช้ Rust เองโดยตรง ไม่เคยรู้สึกว่า lifetime annotation นั้น “ผิด” แม้มันจะดูเป็นงานจุกจิกน่ารำคาญอยู่บ้าง แต่ก็ชินได้เร็ว

    • ตราบใดที่ไม่ใช้ unsafe ใน Rust จะไม่มีสองเธรดมาเขียนหน่วยความจำเดียวกันพร้อมกันได้

    • ฉันไม่ค่อยเข้าใจประเด็น “ทำไม Zig ถึงรู้สึกใช้งานจริงกว่าในเครื่องมือ CLI” เพราะ Rust ก็ยังได้เปรียบในด้านป้องกัน CVE (ช่องโหว่)

    • จริง ๆ แล้วฉันทำงานส่วนใหญ่ด้วยภาษา GC ก็พออยู่แล้ว และเวลาจะไปช่วยโปรเจกต์ภาษาอื่นก็ใช้ได้หมดทั้ง Rust, Zig, C/C++

    • CLI tool มันพิเศษตรงไหนอย่างนั้นหรือ?

      • โดยทั่วไป CLI tool มักไม่ได้มีขนาดใหญ่มาก หรือมักเป็นงานพัฒนาคนเดียว จึงดูแลง่าย
      • Zig หรือ C อาจไม่ค่อยเหมาะกับ codebase ขนาดใหญ่มาก และในโปรเจกต์ที่ซับซ้อนกว่านั้นก็ต้องมี babysitter
      • มีข้อถกเถียงคล้ายกันในอดีตที่ Java ก็ถูกเรียกว่าเป็น “ภาษาแบบ babysitter” แต่ในความจริงหลาย codebase ก็ต้องการสิ่งนั้น
    • การบอกว่าไม่ใช้ unsafe แล้วสองเธรดจะเขียนหน่วยความจำเดียวกันพร้อมกันไม่ได้ มันก็ไม่ได้ชัดเจนสะอาดขนาดนั้น

      • สิ่งที่ฉันอยากได้จากการช่วยของคอมไพเลอร์จริง ๆ คือการแก้ปัญหาเรื่อง memory ordering
      • ในจุดนี้ Rust ยังจัด race บางแบบเป็น safe ไม่ใช่ unsafe
  • เห็นด้วยว่าการทำ backlinks ใน Rust ซับซ้อนเกินไป

    • มันทำได้ด้วย Rc, Weak, RefCell, .borrow() ฯลฯ แต่ก็ไม่ง่าย

    • ถ้าเป็นโปรแกรมอายุสั้น การจัดสรรแบบ arena ก็เป็นอีกทางหนึ่งได้ (ซึ่งอาจตรงกับความหมายของ CLI tool)

    • Rust จะแสดงพลังจริงในแอปพลิเคชันขนาดใหญ่ แบบหลายเธรด และรันยาวนาน

    • ฉันเคยเขียนเมตาเวิร์สไคลเอนต์ขนาดใหญ่ด้วย Rust จริง ๆ และแม้จะรันหลายสิบเธรดตลอด 24 ชั่วโมงก็ไม่มี memory leak หรือแครชเลย

    • ถ้าจะทำสิ่งเดียวกันใน C++ ก็คงต้องมีทีม QA กับเครื่องมืออย่าง Valgrind เป็นของจำเป็น และถ้าใช้ภาษาสคริปต์ก็ช้าเกินไปด้านประสิทธิภาพ

    • ฉันเองก็เคยทำเครื่องบินจำลองฟิสิกส์ด้วย Rust ที่คำนึงถึงทั้งความโค้งของโลกและความแปรผันของแรงโน้มถ่วง

      • ทดสอบเส้นทางยาว 5 ชั่วโมงและ 22 ชั่วโมงได้ต่อเนื่องหลายปีโดยไม่มีปัญหาเลย
      • ตลอด 7 ปีที่ใช้ Rust ฉันแทบไม่เคยเจอแครช มีเพียงไม่กี่ครั้ง ส่วน C/C++ ใช้แค่ตอนต้องแก้โค้ดมรดกเก่าเท่านั้น
  • Zig ก็น่าสนใจอยู่ แต่ D ก็ยังมีอยู่ และส่วนตัวรู้สึกว่า D คือสิ่งแทน C/C++ ที่ฉันต้องการ

    • ไวยากรณ์ของ Zig ดูแปลก ๆ อยู่บ้าง ส่วน Rust ก็กลายเป็นแกนกลางของ ecosystem ไปแล้ว

    • Go เองก็มีส่วนแบ่งสูงในเครื่องมือหลายอย่าง และในสาย AI ก็เป็นรองแค่ Python ในการใช้งาน

    • ก่อน Rust จะมาก็เคยมีการถก Go vs. D และฉันเองก็เคยซื้อหนังสือ D มาเรียน ก่อนจะเปลี่ยนใจไปทาง Go

      • สำหรับฉัน standard library ใช้งานจริงได้ดีกว่ามาก และชื่อชนิดข้อมูลอย่าง int64 ก็ตรงไปตรงมากว่า
    • D ดีอยู่หรอก แต่ยังไม่มี killer app ที่ทำให้แพร่หลาย

  • ฉันไม่ค่อยเข้าใจว่าทำไมต้องยืนยันจะใช้ Rust หรือ Zig เพื่อทำ CLI tool

    • คอขวดคือ I/O ไม่ใช่ว่า GC ช้า

    • ถ้าไม่ใช่งานที่ใช้หน่วยความจำหนักอย่างเกมหรือฐานข้อมูล ฉันคิดว่าประเด็น GC ก็ไม่ใช่ประเด็นหลัก

    • เลยอยากเน้นว่ามากกว่าการถกเรื่อง memory safety ฉันมักคิดถึงเหตุผลว่าทำไมต้องเลือกภาษาที่ไม่ต้องมี GC

    • ถ้าเหตุผลคือ “มันสนุกดีที่ไม่มี GC” แค่นั้นก็เพียงพอแล้ว ไม่จำเป็นต้องเถียงกัน

    • startup time ที่เกิดขึ้นทันทีโดยไม่มีการหน่วงตอนรัน มีประโยชน์มาก

      • เวลาเขียนเครื่องมือแบบ wrapper จะรันเป็นพันครั้งก็ไม่เป็นภาระ
      • และยังแจกจ่ายง่ายขึ้น เพราะไม่มีความซับซ้อนเรื่องการ deploy แบบสภาพแวดล้อมอย่าง Python
    • การทำ CLI ด้วย Go ถือว่าโอเคมาก (แม้ว่าจะไม่ได้ชอบภาษา Go เองนัก)

      • CLI ที่ทำด้วย Python ถ้ามี dependency เยอะจะ deploy ลำบาก และ Rust/Zig ก็ได้รับความนิยมเพราะแจกจ่ายเป็น static binary ได้ง่ายแบบเดียวกับ Go
    • ฉันจะเลือกภาษาที่มี sum type, pattern matching, และรองรับ async เป็นอันดับแรก

      • ต่อให้ไม่ใช่ Rust ความสามารถในการจับ error ตอนคอมไพล์ก็ดีมากอยู่แล้ว
    • สำหรับคำท้วงติงที่ว่าการพัฒนาแบบไม่มี GC ใช้ได้แค่ในสายเกมเท่านั้น

      • ในความจริง เกมมือถือจำนวนมากก็ใช้ GC เหมือนกัน เช่น สภาพแวดล้อม Unity + il2cpp และประสิทธิภาพของ GC เองก็ไม่ได้ดีเสมอไป
    • การถกเรื่อง GC ให้ความรู้สึกเหมือนเป็นการแห่ตามกระแสอย่างหนึ่ง

      • เมื่อ 50 ปีก่อน ก็มีการสร้างเวิร์กสเตชันขนาดใหญ่ที่ใช้ภาษาแบบ GC เป็นฐานได้สำเร็จแล้ว เช่น Interlisp, Cedar
      • ฮาร์ดแวร์ทุกวันนี้แรงกว่าทั้ง CLI ยุค 1970 และแอป Electron มาก แต่เรากลับใช้ประสิทธิภาพนั้นได้ไม่คุ้มพอ
  • ฉันเคยทำเครื่องมือจดโน้ตง่าย ๆ ด้วย borrow/referencing แบบ built-in ของ Rust และมันไม่ได้ซับซ้อนอย่างที่คิด

    • ถ้าจินตนาการเป็นโครงสร้างที่เก็บ index ของรายการ notes แล้วเชื่อมกันด้วย map ความต่างด้านความเร็วก็แทบไม่มี และก็ไม่มีข้อเสียด้านความปลอดภัยด้วย

    • ต่อให้พลาดเรื่อง index ก็ยังโดนจับเป็น out-of-bounds error ได้ ดีกว่าเขียนทับ kernel memory มาก

    • ตอนทำ printf debugging ก็ง่ายและตรงไปตรงมามากกว่า

    • โดยทั่วไป raw pointer หรือ reference ควรใช้เฉพาะจุดที่จำเป็นจริง ๆ อย่าง allocator หรือ async runtime ส่วน logic ทั่วไปแบบอิง index จะเหมาะกว่า

    • นี่ก็เป็นเหตุผลที่มีการพูดกันบ่อยว่า Rust async ใช้ self-referential struct ไม่ได้ จึงมีประเด็นเกี่ยวกับ Pin ตามมา

    • pointer ไปยังค่าที่เก็บอยู่ใน vec จะใช้ไม่ได้ทันทีถ้าเกิด realloc เป็นต้น ซึ่งในกรณีนี้ Miri จะรายงาน error ทันที

  • ถ้าฉันเป็นนักพัฒนา C++ ที่กำลังมองหาภาษาปลอดภัย ฉันคงรู้สึกว่า Swift เหมาะที่สุด

    • ภาษาที่คุ้นเคยหรือคล้ายเดิมจะปรับตัวได้เร็วกว่า

    • ช่วงหลัง Swift ก็เสริมการรองรับข้ามแพลตฟอร์มมากขึ้น และมีคนจากคณะกรรมการมาตรฐาน C++ ปัจจุบันเข้าร่วมหลายคน

    • แต่ด้วยความผูกกับ Apple และการไม่มี native UI framework ที่แข็งแรงในโลกนอก Apple การขยายตัวฝั่ง non-Apple จึงยังน้อยกว่า

    • หวังว่า Swift จะได้รับความนิยมมากขึ้น

    • ถ้ามีแหล่งข้อมูลที่เปรียบเทียบ Swift กับ Zig/C ได้ก็อยากให้แนะนำ

  • มีคนพูดว่า Zig ก็ทำซอฟต์แวร์ที่ memory safe ได้ด้วยความระมัดระวังระดับพอเหมาะ แต่จริง ๆ แล้ว C เองถ้าใช้อย่างมีวินัยในระดับหนึ่งก็ให้ผลแบบเดียวกันได้

    • ท้ายที่สุดปัญหาคือ “วินัย/การฝึกเพียงเล็กน้อย” นี้ไม่เพียงพอในโลกจริง จึงเกิดปัญหาอยู่เสมอ

    • Zig ยังช่วยแก้ปัญหาเพิ่มเติมเหล่านี้ด้วย

      • การเข้าถึงเกินขอบเขต (คิดเป็น 70% ของ CVE ทั้งหมด)
      • null pointer dereference
      • type safety
      • มันดีกว่า C มาก และความผิดพลาดอย่าง use-after-free ก็หลีกเลี่ยงได้ง่ายกว่ามากตราบใดที่ยังไม่ข้ามขอบเขต
      • ระบบ build ข้ามแพลตฟอร์มที่ยอดเยี่ยมของ Zig, การเพิ่มประสิทธิภาพด้วย comptime, และเวลา build ที่เร็วกว่า C++/Rust หลายสิบเท่าก็เป็นจุดแข็ง
      • แม้มาตรฐานไลบรารียังไม่สมบูรณ์และยังมีปัญหาเล็ก ๆ น้อย ๆ แต่คิดว่าอนาคตสดใสมากสำหรับโปรแกรมสาย perf-oriented
    • ถ้า C ล้มเหลวกับปัญหาเรื่อง discipline แบบนี้มานานกว่า 50 ปีแล้ว มันก็คงยากยิ่งกว่า “วิถีเส้าหลิน” เสียอีก