Zig เทียบกับ Rust ในปี 2026
(zackoverflow.dev)- เมื่อราว 3 ปีก่อน ตอนที่ผู้เขียนลงมือเขียน bytecode VM และ garbage collector ด้วย Zig และ unsafe Rust โดยตรงนั้น สรีรศาสตร์ที่เป็นมิตรต่อมนุษย์ ของ Zig ดูเหนือกว่า แต่เมื่อเข้าสู่ยุคของ coding agent ความได้เปรียบนั้นก็แทบไม่มีความหมายอีกต่อไป
- การเพิ่มผลิตภาพของนักพัฒนาประมาณ 1.5–5 เท่าจากฟีเจอร์หลักของ Zig ถูกกลบโดย ผลิตภาพ 100 เท่า ที่ coding agent บน Rust มอบให้
- ฟีเจอร์แกนหลักของ Zig อย่าง allocator interface, integer ความกว้างบิตตามใจ, packed struct และ comptime ล้วนเป็นฟีเจอร์ที่โดดเด่นเมื่อมนุษย์เป็นผู้เขียนโค้ดเองโดยตรง
- ระบบชนิดข้อมูล ของ Rust มีประสิทธิภาพกว่าต่อการป้องกันความผิดพลาดของเอเจนต์ตั้งแต่ขั้นคอมไพล์ ผ่าน bounded polymorphism และการบังคับใช้ invariant
- ในสถานการณ์ที่ปริมาณโค้ดที่เอเจนต์สร้างเพิ่มขึ้น 100 เท่า การรับประกันความปลอดภัยของหน่วยความจำ ของ Rust กลายเป็นข้อได้เปรียบที่ชี้ขาดเมื่อเทียบกับ Zig
การเปลี่ยนแปลงสำคัญ
- Zig เคยมีข้อได้เปรียบอย่างมากด้าน สรีรศาสตร์ของ unsafe code แต่เมื่อสัดส่วนโค้ดที่มนุษย์ลงมือเขียนเองลดลง คุณค่าจากการใช้งานจริงของข้อได้เปรียบนั้นก็ลดลงตาม
- การเพิ่มผลิตภาพของนักพัฒนามนุษย์ระดับ 1.5–5 เท่า ที่ฟีเจอร์ของ Zig มอบให้ ถูกบดบังโดยการเพิ่มขึ้น 100 เท่าเมื่อใช้ coding agent กับ Rust
- ฟีเจอร์เด่นจำนวนมากของ Zig ช่วยเพิ่ม ความสะดวกเมื่อมนุษย์เขียนโค้ดด้วยมือ แต่สำหรับ coding agent ความแตกต่างนี้ไม่ได้สำคัญมากนัก
- เมื่อราว 3 ปีก่อน ผู้เขียนเคยเขียน bytecode VM และ garbage collector ด้วย Zig และ unsafe Rust และรู้สึกว่าประสบการณ์การเขียน unsafe code ฝั่ง Zig ดีกว่า
- แม้ในปี 2026 Zig จะยังเป็นภาษาที่ดี แต่ Rust ได้กลายเป็นภาษาที่ได้รับความนิยมมากกว่าและเข้ากับ coding agent ได้ดีกว่า
อินเทอร์เฟซตัวจัดสรรหน่วยความจำของ Zig
- อินเทอร์เฟซตัวจัดสรรหน่วยความจำ ของ Zig ทำให้สามารถนำตัวจัดสรรเฉพาะทางอย่าง arena หรือ stack fallback มาใช้เพื่อปรับแต่งเส้นทางโค้ดบางแบบให้มีประสิทธิภาพได้ง่าย
- ในกรณีของการอ่านอินพุตจากผู้ใช้หนึ่งบรรทัด ความยาวของอินพุตในทางทฤษฎีนั้นไม่จำกัด จึงต้องใช้ heap allocator แต่ในความเป็นจริงอินพุตส่วนใหญ่มักเป็นคำค้นหาหรือพาธสั้น ๆ ที่เล็กกว่า 1KB มาก
std.heap.stackFallback(256, heap_allocator)จะ วางบัฟเฟอร์ขนาดคงที่ไว้บนสแตก และจะค่อยย้ายไปใช้ฮีปเมื่ออินพุตล้น ทำให้กรณีทั่วไปประมวลผลได้โดยไม่ต้องจองหน่วยความจำบนฮีป- ในอดีต Rust ไม่มีฟังก์ชันที่เทียบได้กับอินเทอร์เฟซ
Allocatorของ Zig ดังนั้นหากต้องการVec<T>ที่ใช้ custom allocator ก็จำเป็นต้องคัดลอก implementation จาก standard library มาแก้ไขเอง - ซอร์สของคอลเลกชันใน Bumpalo มีลักษณะเป็นการ fork standard collection แล้วเชื่อมเข้ากับ bump allocator
- Rust nightly เคยมี
Allocatortrait อยู่พักหนึ่ง และตอนนี้ก็ดูอยู่ในระดับที่ดีเพียงพอแล้ว Allocatorของ Rust ใช้ trait-based static dispatch ขณะที่ตัวจัดสรรของ Zig ใช้ vtable เป็นความแตกต่างสำคัญ- Rust ไม่มี ธรรมเนียมทั้งชุมชน แบบ Zig ที่ออกแบบโครงสร้างข้อมูลให้ยึด allocator parameter เป็นหลัก แต่เมื่อ AI ทำให้การคัดลอกและแก้โค้ดง่ายขึ้น ข้อจำกัดนี้ก็สำคัญน้อยลง
integer ความกว้างบิตตามใจและ packed struct
- integer ความกว้างบิตตามใจ และ
packed structของ Zig ทำให้งานอย่างการปรับแต่ง CPU cache แบบ data-oriented, tagged pointer, NaN boxing และ bitflags ทำได้ง่ายขึ้นมาก - เมื่อใช้ Obj-C API ร่วมกับ Metal ผ่าน Objective-C runtime C API ค่า
idอาจเป็น tagged pointer ไม่ใช่ pointer ไปยัง heap object ที่มีการจัดแนวเสมอไป - หากส่ง
NSNumberแบบ tagged เข้าไปในโค้ดที่สมมติว่ามีการจัดแนว อาจเกิด UB ได้ จึงจำเป็นต้องมีวิธีตรวจแบบต้นทุนต่ำว่า “เป็น heap pointer หรือ tagged immediate” - เลย์เอาต์แบบย่อของ Objective-C tagged pointer คือ บิตต่ำสุด 1 บิตบอกว่า “ไม่ใช่ heap pointer”, 3 บิตถัดไประบุ class slot และอีก 60 บิตที่เหลือเป็น payload
- Zig สามารถใช้
enum(u3)และpacked structเพื่อ แสดง bit layout เป็นชนิดข้อมูลได้โดยตรง เช่นclass: TaggedClass,payload: u60 - ใน Zig สามารถสลับไปมาระหว่าง
u64ดิบกับObjcTaggedPointerด้วย@bitCastและในis_ns_numberก็ตรวจis_taggedกับคลาส.ns_numberได้ - โค้ดฝั่ง Rust ที่เทียบกันได้จะมีค่าคงที่อย่าง
TAG_MASK,CLASS_MASK,CLASS_SHIFT,PAYLOAD_SHIFTอยู่ภายในObjcTaggedPointer(u64)จากนั้นใช้ OR ตอนสร้าง และใช้ mask ตอนเข้าถึง - ใน Rust class slot ไม่ใช่ชนิดข้อมูลจริง แต่เป็นค่าคงที่
u64และการเขียนด้วยมือลักษณะนี้ มีสรีรศาสตร์ด้อยกว่า Zig - ใน Rust มักเหมาะกว่าหากใช้ crate อย่าง bitfield หรือ bitflags แต่ทั้งคู่ต่างพึ่งพา proc macro และยังไม่ให้ความรู้สึกดีเท่า
packed structของ Zig - เมื่อมี coding agent ปัญหาความน่าเบื่อของการต้องเขียนโค้ดแบบนี้ด้วยมือก็ลดลงอย่างมาก
คุณค่าที่เปลี่ยนไปของ comptime
- comptime ของ Zig เป็นฟีเจอร์ที่โดดเด่นที่สุด และหากไม่นับภาษาประเภท dependent type ที่ซับซ้อนบางภาษา ก็แทบไม่มีภาษาไหนให้ความสามารถด้าน compile-time evaluation ได้ดีเท่า Zig
- ในการใช้งานจริง ผู้เขียนกลับไม่ได้รู้สึกคิดถึง comptime มากนัก และประมาณ 95% ของการใช้งาน คือการสร้าง generic data structure แบบ parameterized type
- แพตเทิร์นอย่าง
fn ArrayList(comptime T: type) typeซึ่งรับ type เข้ามาแล้วคืน struct type ที่มีitems: []T,capacity: usize,allocator: Allocatorถือเป็นตัวอย่างที่เด่นชัด - ระบบชนิดข้อมูลของ Rust สามารถทดแทน generic แบบ comptime สไตล์ Zig ได้เป็นส่วนใหญ่ และยังบังคับใช้เงื่อนไขคงตัวได้มากกว่า
- สำหรับอีกราว 5% ที่เหลือ การไม่มี comptime ทำให้ไม่สะดวก และทางเลือกทดแทนที่เชื่อถือได้มีเพียง codegen
- ตอนพัฒนาเกม หากต้องการ hardcode ข้อมูล hitbox geometry ที่สร้างมาจากเครื่องมือให้ลงไปอยู่ใน data structure ใน Rust ก็ต้องให้ Claude ช่วยเขียนสคริปต์ที่สร้างไฟล์ Rust ขึ้นมา
- ถึงอย่างนั้น compile-time evaluation ก็ไม่ได้เป็นสิ่งที่จำเป็นบ่อยนักในทางปฏิบัติ
ข้อดีของระบบชนิดข้อมูลของ Rust
- ระบบชนิดข้อมูลของ Rust ถูกมองว่าเป็นสิ่งที่คุ้มค่ากว่าเมื่อแลกกับ comptime ของ Zig โดยเฉพาะในด้าน traits/typeclasses สำหรับ bounded polymorphism
- หากพยายามทำ bounded polymorphism ระดับเดียวกันใน Zig จะยากมาก
- ระบบชนิดข้อมูลของ Rust สามารถบังคับใช้ เงื่อนไขคงตัว (invariant) ได้มากกว่า จึงช่วยป้องกันข้อผิดพลาดที่ coding agent มักทำได้ดี
- ในโค้ดเกม ผู้เขียนใช้ crate euclid เพื่อป้องกัน ความสับสนของ coordinate space ซึ่งเป็นปัญหาที่พบบ่อยในงานกราฟิก
- หากสร้างชนิดข้อมูลเฉพาะสำหรับแต่ละ coordinate space อย่าง
Point<Screen>หรือPoint<World>ก็จะป้องกันการเผลอเอา world coordinate ไปปนกับ screen coordinate ได้ตั้งแต่ขั้นคอมไพล์ - หากแยก
WorldPoint,WorldVector,ScreenPointเป็นคนละประเภท การบวก point กับ vector ภายใน space เดียวกันก็ยังทำได้ Translation2D::<f32, WorldSpace, ScreenSpace>ทำให้สามารถ แปลงจาก world space ไปยัง screen space อย่างชัดเจน ได้- ในทางกลับกัน โค้ดอย่าง
let bad: ScreenPoint = player;ที่พยายามนำWorldPointไปใส่ในScreenPointโดยตรงจะไม่ได้รับอนุญาต
ผลของการต้องจัดการปัญหาหน่วยความจำน้อยลง
- หาก coding agent ทำให้สามารถ เขียนโค้ดได้มากขึ้น 100 เท่า ปริมาณโค้ด Zig ที่ต้องตรวจปัญหาด้านหน่วยความจำก็จะเพิ่มขึ้น 100 เท่าด้วย
- หากไม่มีการตรวจสอบเชิงรูปแบบ พื้นที่การค้นหา ที่ต้องไล่ดูเพื่อหาบั๊กก็จะกว้างขึ้นมาก
- ในสถานการณ์ปัจจุบันที่ปริมาณโค้ดที่ถูกสร้างมีมากขึ้น Rust จึงน่าสนใจกว่า
- trade-off ดั้งเดิมของ Rust คือ borrow checker อาจลดผลิตภาพของนักพัฒนาเมื่อยังไม่คุ้นเคย แต่เมื่อมี coding agent ข้อเสียนี้ก็สำคัญน้อยลงมาก
- แม้จะใช้
unsafeใน Rust ก็ยังสามารถให้ coding agent รันเครื่องมืออย่าง miri เพื่อตรวจว่าไม่มี UB เกิดขึ้น และไม่ได้ละเมิดกฎ aliasing ของ Rust
บทสรุป
- Zig ยังคงเป็นภาษาที่นึกถึงด้วยความเสียดายและเป็นภาษาที่ดี
- แต่สำหรับวิธีการทำงานในปี 2026 Rust เป็นตัวเลือกที่ได้รับความนิยมมากกว่า และเข้ากับ coding agent ได้ดีกว่า
1 ความคิดเห็น
ความคิดเห็นจาก Lobste.rs
หัวหน้าทีมคนก่อนเคยมีความเชื่อค่อนข้างแรงกล้าว่า โค้ดก๊อปแปะ ไม่ได้แย่เสมอไป
เพราะหลักการ DRY ฟังดูเหมือนผิดสัญชาตญาณหรือชวนถกเถียง แต่เขาเป็นคนที่ปฏิบัติจริงมาก และมักใช้หลักนี้กับโค้ดเบสทดสอบขนาดใหญ่
ตรรกะคือ แทนที่จะฝืนสร้างอินเทอร์เฟซกลางอันชาญฉลาด โค้ดเบสที่เรียบง่ายกว่าแต่ใหญ่กว่าและมีความซ้ำซ้อนมากกว่าอาจดูแลง่ายกว่า
ช่วงนี้พอใช้ LLM ก็กลับมาคิดแบบเดียวกัน และตอนนี้เริ่มเอาไปใช้กับส่วนของซอฟต์แวร์ที่สำคัญกว่านั้นด้วย
การสร้างโค้ดทำได้เร็ว และ LLM ก็น่าจะทำได้ดีกว่ากับ โค้ดเบสที่เรียบง่ายแต่ซ้ำซ้อนมาก
ถ้าใส่ abstraction ในเทสต์มากเกินไปเพื่อลดความซ้ำซ้อน จะทำให้เทสต์เข้าใจยากขึ้น และเสี่ยงจะผิดแบบแนบเนียนด้วย
ที่แย่กว่านั้นคือถ้านำ abstraction ของโค้ดที่กำลังทดสอบกลับมาใช้ เทสต์ก็อาจผิดในแบบเดียวกับโค้ดนั้นได้
อีกอย่าง เทสต์ต่างจากโค้ดแอปพลิเคชันตรงที่มัน “compose” ได้แทบจะฟรี
ถ้าไม่ได้ทำ test harness พังหนัก คุณจะเพิ่มหรือลบเทสต์ตามใจโดยไม่กระทบเทสต์อื่น และไม่มีแรงเสียดทานด้านการรวมเข้าด้วยกัน จึงมีเหตุผลน้อยลงอีกข้อที่จะต้องหลีกเลี่ยงความซ้ำซ้อน
ในบริบทของเทสต์ เคยเห็นแนวคิดนี้ถูกเรียกว่า DAMP: “Descriptive and Meaningful Phrases” เป็นหลักที่เน้นความอ่านง่ายมากกว่าความไม่ซ้ำ
หลักนี้อาจทำให้เกิดความซ้ำซ้อนในรูปของโค้ดคล้ายกันซ้ำไปมา แต่ช่วยให้เทสต์ดูชัดเจนขึ้นว่าเขียนได้ถูกต้อง
https://testing.googleblog.com/2019/12/…
ในคอมมูนิตี้ Go ก็มีคำพูดคล้ายกันว่า “การก๊อปเล็กน้อยดีกว่าการพึ่งพาเล็กน้อย” https://go-proverbs.github.io/
รู้สึกขอบคุณที่เขาแชร์การไลฟ์โค้ดดิ้ง
ถ้าจำไม่ผิด เวลาจะเริ่มทำอะไรสักอย่าง เขามักหาโค้ดที่ใกล้เคียงที่สุดกับสิ่งที่จะสร้าง แล้วก๊อปมาทั้งก้อนก่อนค่อยแก้
ตอนแรกก็คิดว่า “ไม่ควรนั่งคิด abstraction ที่สองอย่างนี้ใช้ร่วมกันได้ก่อนเหรอ?” แต่เขาก็แค่ก๊อปแปะแล้วเดินหน้าต่อ และมีประสิทธิภาพกว่าฉันมาก
พอมองในจังหวะที่ Bun กำลัง เขียนใหม่จาก Zig → Rust ด้วย AI มันก็น่าสนใจดี https://xcancel.com/jarredsumner/status/2053063524826620129#m
ในจำนวนนั้นมี 75 อันที่ถ้าเป็นภาษาแบบมี destructor, move semantics, และ borrow checker ก็คงคอมไพล์ไม่ผ่าน
เท่ากับว่าในทุก ๆ 3 PR ที่ปล่อยออกมา จะมี 1 PR เป็นแนว “ลืม free ใน error path”
จาก 108 อันนั้น ราว 88 อันอยู่ฝั่ง Zig ส่วนราว 14 อันทาง C++ ส่วนใหญ่เป็นหมวดตกค้างที่ไม่ว่าภาษาไหนก็ยังเจอได้ เช่น reference cycle และ race ของ GC concurrency
เพราะงั้นความต่างระหว่าง Zig→Rust มีอยู่จริง และบั๊กฝั่ง Zig ก็เป็นชนิดที่แก้ได้ตรง ๆ ด้วย destructor และ ownership ขณะที่ฝั่ง C++ ลงมาจนใกล้พื้นแล้ว
ถ้าไม่มีการรับประกันที่แข็งแรงกว่านี้ในระดับ compile time มันก็จะยังเป็นเกมไล่จับกันต่อไป
ข้อเสนอคือแทนที่จะคอยไล่แก้บั๊กหมวดใหญ่ที่สุดเป็นรายตัว ก็เอามันออกไปเชิงโครงสร้างเลย
– bun/docs/rust-rewrite-plan.md at claude/phase-a-port · oven-sh/bun · GitHub
ตรงที่บอกว่า “อีก 5% ที่เหลือ ถ้าไม่มี comptime จะทรมาน และวิธีเดียวที่จะไปถึงผลลัพธ์เทียบเท่าได้อย่าง reliably คือ code generation” นั้น ไม่ค่อยชัดว่าผู้เขียนหมายถึงอะไร
เพราะเขาไม่ได้พูดถึง procedural macro เลย
การทำให้ดีอาจยุ่งนิดหน่อย แต่ก็ทำอะไรได้เยอะมาก
ฉันคิดว่า code generation เองก็โดนมองในแง่ลบเกินเหตุอยู่บ้าง
ฉันเคยใช้สคริปต์
build.rsแก้ปัญหาน่าปวดหัวหลายอย่างด้วย code generation และมันก็ใช้งานได้ดีแน่นอนว่าในอนาคตอาจมานั่งเสียใจก็ได้
ข้ออ้างหลักของบทความดูเหมือนประมาณนี้
Rust เป็นภาษาที่ดีจริง แต่ก็ยังรู้สึกว่าอันนี้เกินไปหน่อย
ดูเหมือนโฆษณา coding agent
นี่มาจากบทความที่ลิงก์ไว้ของผู้เขียนคนเดียวกัน: ความผิดพลาดที่พบบ่อยมากในงานกราฟิกโปรแกรมมิงคือการสับสน coordinate space และ type system ก็ทรงพลังพอจะใช้ type แทนได้ว่าพิกัดหรือ transformation แบบไหนใช้ได้
เรื่องเดียวกันนี้ก็ใช้ได้กับสกุลเงิน, หน่วย SI กับระยะทาง/น้ำหนักแบบ yard-pound, สตริงที่ผ่านการตรวจสอบแล้วกับสตริงจากผู้ใช้ รวมถึงค่าความลับ
และถ้าจัดการดี ๆ ก็ใช้ state type ป้องกันสถานะที่เป็นไปไม่ได้หรือไม่ sound ได้ด้วย
แต่ถ้าพูดเป็นการส่วนตัว สิ่งที่ฉันอยากได้จาก Rust มากที่สุดคือ การกำจัด data race แบบสมบูรณ์
แม้แต่ภาษา managed ก็ยังมี data race
คำแนะนำแบบ “ก็ใช้ Go สิ” นั้น ใน Go ทุกอย่าง mutable ผ่านการอ้างอิงระหว่างเธรดได้หมด แถมยังมี slice gymnastics อีก
แม้แต่ JavaScript ซึ่งเคยเป็นภาษาของเล่นสุด ๆ และอยู่ฝั่งที่บริสุทธิ์ปลอดภัยกว่า ก็ยังมีทุก
awaitที่เป็น race ที่อาจเกิดขึ้นได้ยังไม่ต้องพูดถึงความชั่วร้ายของแพตเทิร์น everything-is-an-EventEmitter
เพราะงั้นใช่เลย ถ้ามีแค่ GC อย่างเดียวก็คง… 🤫
รู้สึกเหมือนกำลังซ่อนประเด็นสำคัญไว้นิดหน่อย
coding agent นั้นเก่งกับ Python และ JavaScript มากเหมือนกัน
จะดีกว่า Rust หรือไม่นั้นเป็นเรื่องความเห็นล้วน ๆ แต่ถึงอย่างนั้นฉันก็คงไม่เลือกสองภาษานั้นสำหรับงานจำนวนมากอยู่ดี
เลยสงสัยว่าปัญหาอยู่ที่ฟีเจอร์ของ Zig เปลี่ยนบ่อย หรือแค่เป็นภาษาใหม่กว่าจน ข้อมูลฝึก AI ยังปนเปื้อนกันแน่
รู้สึกว่า Zig เขียนยากกว่า Rust แต่กลับอ่านง่ายกว่า
ในยุค AI เราอ่านโค้ดมากกว่าเขียนโค้ด ฉันเลยเริ่มเอนเอียงไปทาง Zig
เมื่อดูจากปริมาณโค้ดที่กำลังถูกสร้างขึ้นตอนนี้ การบอกว่า Rust น่าสนใจกว่าก็เป็นแค่ก้าวแรก
ยิ่งคอมพิวเตอร์เป็นฝ่ายเขียนโค้ดมากขึ้นเท่าไร ภาษาแบบ เป็นทางการมากขึ้น ก็จะยิ่งได้เปรียบ
มันดูเหมือนอีกช่วงหนึ่งของข้อถกเถียงเรื่อง dynamic typing
ประมาณว่า “dynamic type ง่ายกว่าสำหรับมนุษย์ แล้วทำไมต้องระบุสิ่งเดิมซ้ำสามรอบเพื่อเครื่องด้วย?”
ทั้ง type, lifetime… แล้วอะไรอีกบ้างที่ทำให้เครื่องเขียนและอ่านเข้าใจได้ง่ายกว่า
น่าสงสัยว่าในอนาคต ภาษาที่คอมพิวเตอร์ใช้เขียนโค้ดจะทำให้มนุษย์เขียนด้วยมือตรง ๆ ได้ยากขึ้นแค่ไหน