2 คะแนน โดย GN⁺ 2025-08-25 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ใน Zig เวอร์ชัน 0.15 ได้มีการนำ อินเทอร์เฟซ IO ใหม่ (std.Io.Reader, std.Io.Writer) เข้ามาใช้
  • มีเป้าหมายเพื่อปรับปรุงความซับซ้อนของวิธี IO แบบเดิมและปัญหาด้านประสิทธิภาพ แต่กลับเกิด ความสับสนในการใช้งานจริง
  • การใช้ tls.Client และ buffer มีรูปแบบการส่งพารามิเตอร์ที่ไม่สอดคล้องกัน จึงยิ่งเพิ่มความสับสน
  • แม้แต่การทำตัวอย่างการใช้งานพื้นฐานก็ยังมีข้อกำหนดที่ซับซ้อน เช่น การระบุ ขนาดบัฟเฟอร์หลายแบบและฟิลด์ออปชัน
  • เอกสารทางการ ตัวอย่างโค้ด และฟังก์ชันอำนวยความสะดวกยังมีไม่เพียงพอ ทำให้ ไม่เป็นธรรมชาติสำหรับผู้เริ่มต้น

อินเทอร์เฟซ IO ใหม่ที่นำมาใช้ใน Zig 0.15 และที่มา

  • ใน Zig เวอร์ชัน 0.15 ได้มีการนำประเภท IO ใหม่คือ std.Io.Reader และ std.Io.Writer เข้ามาใช้
  • อินเทอร์เฟซ IO แบบก่อนหน้าก่อให้เกิดความซับซ้อนจากปัญหาด้านประสิทธิภาพ การปะปนของประเภท และการใช้ anytype มากเกินไป
  • เป้าหมายหลักของโครงสร้าง IO ใหม่คือการแยกประเภทระหว่างอินเทอร์เฟซให้ชัดเจนและปรับปรุงประสิทธิภาพ

ปัญหาจริงในการใช้ tls.Client และอินเทอร์เฟซ IO

  • ระหว่างการอัปเดตไลบรารี smtp เดิม ได้เกิดความสับสนกับวิธีใช้ฟังก์ชัน tls.Client.init
  • ตามเอกสาร ฟังก์ชัน init ระบุว่าต้องรับ พอยน์เตอร์ของ Reader และ Writer พร้อมชุดออปชัน เป็นอาร์กิวเมนต์
  • net.Stream ของ Zig จะคืนค่า Stream.Reader/Writer ผ่านเมธอด reader() และ writer() ตามลำดับ
    • แต่ Stream.Reader/Writer กับ std.Io.Reader/Writer ไม่ใช่ประเภทเดียวกันแบบตรงตัว จึงต้องมีการแปลง
    • ฝั่ง Reader ต้องเรียกเมธอด interface() ส่วน Writer ต้องใช้ฟิลด์ &interface จึงดูขาดความสอดคล้อง
โฆษณา

ปัญหาเรื่องการตั้งค่าบัฟเฟอร์และฟิลด์ออปชัน

  • stream.writer, stream.reader ต่างก็รับบัฟเฟอร์เป็นอาร์กิวเมนต์
    • Buffer ถูกเน้นว่าเป็นองค์ประกอบจำเป็นในอินเทอร์เฟซ IO ใหม่นี้
  • เมื่อเรียก tls.Client.init จำเป็นต้องมี ฟิลด์ออปชัน 4 รายการ ได้แก่ ca_bundle, host, write_buffer, read_buffer
    • กฎในการแยกค่าว่าบางส่วนส่งผ่านพารามิเตอร์ออปชัน และบางส่วนส่งเป็นอาร์กิวเมนต์โดยตรง ให้ความรู้สึกว่าไม่ชัดเจน
var tls_client = try std.crypto.tls.Client.init(
  reader.interface(),
  &writer.interface,
  .{
    .ca = .{.bundle = bundle},
    .host = .{ .explicit = "www.openmymind.net"; } ,
    .read_buffer = &read_buf2,
    .write_buffer = &write_buf2,
  },
)
  • ในทางปฏิบัติ หากไม่ได้ส่งพอยน์เตอร์ของบัฟเฟอร์อย่างถูกต้อง โปรแกรมอาจทำงานผิดปกติ หรือเกิดอาการค้างและล่มได้หลากหลายรูปแบบ
โฆษณา

ปัญหาด้านความเป็นธรรมชาติเมื่อใช้ Reader

  • แม้ฟิลด์ reader ของ tls.Client เองจะเป็น "สตรีมที่ถอดรหัสแล้ว" แต่ใน std.Io.Reader กลับไม่มีเมธอด read แบบทั่วไป
  • กลับมีเพียงเมธอดที่เข้าใจได้ยากกว่า เช่น peek, takeByteSigned, readSliceShort
  • API ที่พอใกล้เคียงกับการใช้งานทั่วไปมากที่สุดคือการอ่านข้อมูลเข้าไปยังบัฟเฟอร์ผ่านเมธอด stream
var buf: [1024]u8 = undefined;
var w: std.Io.Writer = .fixed(&buf);
const n = try tls_client.reader.stream(&w, .limited(buf.len));

ตัวอย่างโค้ดทั้งหมดและปัญหาในการใช้งานจริง

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

ประสบการณ์และข้อสรุป

  • การเปลี่ยนชื่ออย่าง std.fmt.printInt รวมถึงการเปลี่ยนแปลงด้านการออกแบบ API ทำให้กระบวนการ migration ไม่ใช่เรื่องง่าย
  • ได้พบความยากซ้ำ ๆ หลายจุด เช่น รูปแบบ reader.interface(), &writer.interface, วิธีส่งออปชัน และความจำเป็นต้องใช้บัฟเฟอร์หลายชุด
  • สำหรับผู้ที่ไม่คุ้นเคยกับโปรโตคอลเครือข่ายหรือความปลอดภัยอย่าง TLS การทำความเข้าใจข้อกำหนดยิ่งรู้สึกยากขึ้นไปอีก
  • โดยสรุปแล้ว เมื่อเทียบกับเดิม ยังมีหลายส่วนที่ ขาดความชัดเจน การจัดทำเอกสาร และความสะดวกในการใช้งาน

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

 
GN⁺ 2025-08-25
ความคิดเห็นจาก Hacker News
  • ขอแจ้งว่าเป็นผู้เขียนเอง ในที่สุดก็ทำให้มันทำงานได้ถูกต้อง ทั้ง writer แบบเข้ารหัสและ stream writer ต่างก็ต้องมีขั้นตอน flush และในฝั่งอ่านก็มีปัญหาอยู่ด้วย สตรีมมิงนั้นทำงานได้ แต่เพราะ Writer.Fixed ไม่ได้ implement sendFile การอ่านครั้งแรกจึงคืนค่า 0 เสมอ หลังจากเรียกครั้งแรก ภายในจะสลับจากโหมดสตรีมมิงไปเป็นโหมดอ่าน ทำให้จู่ ๆ ทุกอย่างก็เริ่มทำงานได้หมด (ลิงก์โค้ดที่เกี่ยวข้อง: Zig File.zig #L1318) ตอนนี้กำลังพยายามเปิดฟีเจอร์บีบอัดกลับเข้าไปในไลบรารี websocket

    • ทำให้นึกถึงมีม YouTube ว่า "อย่าลืม flush" (วิดีโอ YouTube)

    • ชักสงสัยว่า principle of least surprise หายไปอยู่ไหน

    • รู้สึกทึ่งมากที่ย้ายจากอินเทอร์เฟซก่อนหน้ามาเป็นสภาพแบบตอนนี้ได้ น่าประหลาดใจสุด ๆ

  • ไม่ได้เป็น PM ของ Zig แต่ทางออกแรกที่ชัดเจนที่สุดสำหรับปัญหาที่ OP เจอ ก็คือทำเอกสารให้ดีกว่านี้และเพิ่มตัวอย่างการใช้งานให้มากขึ้น (จะเยอะแค่ไหนก็ไม่เป็นไร) การทำแบบนั้นอาจเป็นโอกาสดีในการทบทวนด้วยว่ากำลังให้ผู้ใช้ต้องทำอะไรเยอะเกินไปหรือเปล่า ถ้าเป้าหมายคือประสิทธิภาพสูงสุดแบบถึงที่สุด หรือหลีกเลี่ยงการใส่ abstraction ที่ทำให้ performance ลดลง ก็ดูเหมือนจะทำสำเร็จแล้ว แต่ DX นั้นเหมือนหลุดไปถึงกาแล็กซีแอนดรอเมดา

    • ดูเหมือนคุณจะยังไม่ค่อยรู้จักวัฒนธรรมของคอมมูนิตี้ Zig ถ้าบ่นว่าเอกสารไม่พอ ทุกคนก็พร้อมจะเข้ามาคอมเมนต์ว่า "ไปอ่านโค้ด stdlib เองสิ" API ส่วนใหญ่ใช้งานยากแบบที่บทความนี้เล่า และแม้แต่งานพื้นฐานอย่าง HTTP หรือไฟล์ซิสเต็ม ถ้าไม่คุ้นก็ลำบากมาก เพราะงั้นจึงเหมือนเหลือรอดเฉพาะคนที่เก่งจริง ๆ

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

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

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

    • ฉันไม่ใช่นักพัฒนา Zig แต่คิดว่าเหตุผลหนึ่งที่เอกสารของ Zig กระชับมากเกินไป คือภาษายังใหม่และยังพัฒนาเปลี่ยนแปลงอยู่ตลอด จึงเข้าใจได้ว่ามันยากจะทุ่มเวลาและพลังไปกับการเขียนเอกสาร ทั้งที่รู้อยู่แล้วว่าอีกไม่นานอาจไม่ถูกต้องแล้ว

  • ตัวภาษา Zig เองดีมากจริง ๆ แต่ standard library ยังไม่เสร็จอีกเยอะ เปลี่ยนตลอด ขาดอีกมาก และบางส่วนก็ abstraction เยอะเกินไป ขณะที่บางส่วนก็ low-level เกินไป ตอนนี้ผมคิดว่าใช้ OS API ตรง ๆ ยังดีกว่าใช้ standard library ถ้าไม่ได้ตั้งใจเป็นเบต้าเทสเตอร์ ก็แนะนำให้เลี่ยง standard library ไปก่อน

    • จริง ๆ เวลาผมใช้ zig ก็มักจะเน้น OS API เหมือนกัน cImports ทำมาได้ดี เลยหยิบมาใช้ได้ง่ายเวลาขี้เกียจเขียนนิยามของ zig เอง

    • ในมุมมองผม Zig พยายามทำหลายอย่างเกินไปพร้อมกัน จนไปไม่ถึงแม้แต่เส้นคุณภาพขั้นต่ำที่ผมคิดไว้ มันดูเหมือนบีบให้ผู้ใช้ต้องยอมรับการเปลี่ยนแปลงรุนแรงและการทดลองต่าง ๆ จนต้องมีคนจำนวนมากพอคอยลงทุนกับภาพฝันว่า "ก่อน 1.0 จะพังยังไงก็ได้ สักวันมันจะดีขึ้นเอง" (ซึ่งผมสรุปว่า วันนั้นคงไม่มาถึง) ผมไม่คิดว่าการผลักภาระการทดลองของตัวเองให้คนอื่นเป็นเรื่องดี ต่อให้เตือนไว้ล่วงหน้าว่ายังไม่เสถียร หรือบอกว่าอย่าพึ่งพามัน คนที่เจอ rug pull อยู่ดี ก็ยังเดือดร้อนอยู่ดี ผมไม่แน่ใจว่า zig คืออะไรกันแน่ Matklad เรียกมันว่า machine level language (บทสัมภาษณ์ที่เกี่ยวข้อง: lobste.rs - สัมภาษณ์ Matklad) แต่หน้าเว็บทางการบอกว่าเป็นภาษา general-purpose ที่ robust, optimal, reusable ซึ่งสองอย่างนี้ขัดกันเอง และยังมีปัญหาจำนวนมากที่ไม่จำเป็นต้องจัดการหน่วยความจำเอง ดังนั้น zig จึงไม่ใช่ภาษาอเนกประสงค์จริง ๆ ความสับสนทั้งหมดนี้สะท้อนออกมาในความไม่เสถียรของ zig และ standard library ที่ใหญ่เทอะทะ การอ้างว่าทั้งเรียบง่ายและอเนกประสงค์ แต่มีไลบรารีใหญ่ขนาดนี้เป็นเรื่องย้อนแย้ง Async ก็เหมือนถูกสัญญาว่าเป็นคำตอบสารพัด ทั้งที่มันไม่ใช่ความสามารถที่ทำได้อย่างมีประสิทธิภาพแบบทั่วไปในทุกแพลตฟอร์ม สมัยก่อนยังโปรโมตว่ามันแก้ปัญหา function coloring ได้ แต่ความพยายามนั้นก็ถูกทิ้งไปแล้ว ตรรกะที่ให้เชื่ออีกครั้งว่าคราวนี้จะทำได้จริงดูแปลกมาก ที่จริงแล้วถ้าต้องการแค่คอมไพเลอร์ที่ทำงานได้ทุกแพลตฟอร์ม ก็แค่มีชุดคำสั่งแอสเซมบลีพื้นฐานที่ใช้สร้างคอมไพเลอร์ได้ก็พอ luajit ถึงขั้นเขียน parser เป็นแอสเซมบลีล้วนและยังทำงานได้ดีทุกที่ ผมเขียนโปรแกรมส่วนใหญ่ด้วย lua และแทบไม่เคยเจอบั๊กในอินเทอร์พรีเตอร์เลย นึกไม่ออกด้วยว่า zig จะแก้ปัญหาอะไรได้ดีกว่า luajit แม้จะมีบางอย่างที่ต้องใช้ zig จริง ๆ ก็ embed ส่วนนั้นเข้าไปในโค้ด lua แล้วต่อผ่าน FFI ได้ โค้ดส่วนใหญ่ไม่ได้ต้องการการปรับแต่ง low-level ขนาดนั้นจริง ๆ การเอา zig เข้ามากลับทำให้ปวดหัวกว่าเดิม ทุกวันนี้ความคาดหวังเกินจริงต่อ zig ชวนให้นึกถึงช่องว่างระหว่าง AI กับความเป็นจริง ถ้าจะเชื่อ zig ก็ต้องเชื่อความหวังลม ๆ แล้ง ๆ ว่ามันจะมีความสามารถที่ตอนนี้ยังไม่มีในสักวัน ทั้งที่ไม่มีแผนปฏิบัติการชัดเจน มีแต่แนวว่า "รออีกหน่อยสิ"

  • ผมไม่เข้าใจว่าทำไมไลบรารีหรืออินเทอร์เฟซต้องการให้ผมจัดสรรบัฟเฟอร์ของไทป์นั้นเอง ถ้าผมต้อง parse เองอยู่แล้ว ก็ไม่ต้องมีไลบรารีก็ได้ และถ้าใช้จริง มันก็อาจทำให้การแลกเปลี่ยนพังได้ อินเทอร์เฟซแปลก ๆ ของ go มีอยู่เพราะบางอินเทอร์เฟซขยาย writer interface ออกไป (ดู hijacker interface) หรือเพราะ request object ถูกนำกลับมาใช้ใหม่ได้หลายแบบในหลาย middleware สรุปคือ request ไม่จำเป็นต้องขยายได้ แต่ response สามารถเปลี่ยนรูปได้หลายแบบ เช่น websocket หรือ wrapper ของ TCP

    • ผมไม่ได้รู้สึกว่าไลบรารีที่ให้จัดสรรบัฟเฟอร์จากภายนอกเป็นเรื่องแปลก มันให้ความยืดหยุ่นมากขึ้น แลกกับงานทำมือที่มากขึ้น ตัวอย่างเช่น ถ้าคุณมี buffer pool ที่ทำไว้แล้ว คุณก็อาจอยากใช้ซ้ำ ถ้าไทป์นั้นจัดสรรเองภายใน ก็จะทำแบบนั้นไม่ได้ หรือในสภาพแวดล้อมที่ต้องจัดสรรทุกทรัพยากรไว้ล่วงหน้า ก็อาจไม่มีสิทธิ์จัดสรรทีหลังได้ ข้อเสียคือมีผู้ใช้แค่ 10% ที่ต้องการความยืดหยุ่นแบบนี้ แต่อีก 90% แค่จะจัดสรรบัฟเฟอร์แล้วส่งเข้าไป ทำให้ทุกคนต้องรับความซับซ้อนเพิ่ม วิธีที่ดีที่สุดคือน่าจะเปิดให้ยืดหยุ่นสูง แต่กรณีง่าย ๆ ก็ยังใช้ง่ายได้ เช่น ส่งบัฟเฟอร์ความยาว 0 (หรือ null ของ Zig) เข้าไป แล้วให้ไทป์จัดสรรเอง และมี constructor แบบง่ายที่สร้างได้โดยไม่ต้องมีบัฟเฟอร์ด้วย แน่นอนว่าสิ่งเหล่านี้ก็เพิ่มภาระงานเอกสารเต็ม ๆ

    • มันคล้ายความต่างด้านธรรมเนียมที่แต่ละภาษาเลือกใช้ (ประมาณเรเดียน/องศา) IO แบบไหนก็แปลงกันได้อย่างอิสระ ฝั่งหนึ่งอาจเรียกมันว่า mock อีกภาษาหนึ่งอาจเรียกว่า unsafeFoo ก็ได้ Andrew Kelley ไปค้นพบแพตเทิร์นที่คอมมูนิตี้ Haskell ถกกันมา 30 ปีด้วยตัวเองในไลฟ์สตรีม ดังนั้นอนาคตก็คือ Zig เขาเข้าใจก่อนคนอื่น

    • ความหมายของ external buffer ก็คือฟังก์ชันจะข้ามการจัดสรรบัฟเฟอร์ไป

  • ผมไม่มีแผนจะอัปเกรด side project ที่เขียนด้วย zig ไปเป็น 0.15.x เข้าใจนะว่าทำไม Andrew ถึงเลือกออกรีลีสและปล่อย Io ใหม่ให้กลุ่ม early adopter ได้ลอง แต่เพิ่งผ่านการเปลี่ยน readers/writers ครั้งใหญ่มาได้ไม่กี่วันเอง สำหรับคนทำงานกับ standard library นี่เป็นเรื่องดี แต่สำหรับคนที่ใช้ zig เป็นงานอดิเรกแบบผม ดูจะฉลาดกว่าถ้ารอถึง 0.16.0 หลังจากมันนิ่งแล้ว

    • ถ้าภาษาชื่อ Zig ก็อดนึกมุกไม่ได้ว่า บางทีก็ควร Zag บ้างเหมือนกัน

    • Loris Cro ซึ่งเป็นสมาชิกแกนหลักของ Zig ก็พูดในบทสัมภาษณ์ล่าสุดเหมือนกันว่าจะเลื่อนการอัปเดต zig ในโปรเจ็กต์ของตัวเองออกไปจนกว่าผลกระทบจากการเปลี่ยน IO จะสงบลง แต่เขายังมองภาพรวมในแง่บวก ทั้ง Andrew และ Loris ต่างก็เห็นว่านี่น่าจะเป็นการเปลี่ยนแปลงใหญ่ครั้งสุดท้าย จึงคาดหวังได้ว่า 1.0 คงอยู่ไม่ไกล ตัวแปรใหญ่ที่สุดตอนนี้มีแค่ผลกระทบจาก stack-less coroutine ที่กำลังจะถูกนำกลับมาใช้ใหม่

  • หลังจากอ่านโพสต์เรื่องอินเทอร์เฟซ IO ใหม่ ผมก็ตัดสินใจเลี่ยง zig ไปเลย รู้สึกว่าอย่างน้อยสัญชาตญาณของผมก็ถูก ถึงเหตุผลจะต่างกัน แต่สุดท้ายมันให้ความรู้สึกซับซ้อนไม่ต่างจากความยืดเยื้อก่อนยุค C++11 เหมือนกำลังเห็นแพตเทิร์นเดิม ๆ ที่ภาษาใหม่พยายามเข้ามาแทนที่ของเก่า แต่สุดท้ายก็ซับซ้อนพอ ๆ กัน

    • ขอแจ้งว่าโพสต์ของผมก็เป็นหนึ่งในนั้น แต่ผมไม่คิดว่าคนควรจะกลัวจนไม่กล้าลอง zig เพราะบทความแบบนี้ ทีม zig พร้อมเปลี่ยนอย่างจริงจังถ้าเห็นทางออกที่ดีกว่า ถ้ามอง zig เป็นสิ่งที่จะเลี้ยงชีพคุณในอนาคต การเปลี่ยนแปลงพวกนี้อาจไม่เหมาะ แต่สำหรับคนคนเดียวหรือทีมเล็ก ๆ มันก็ยังเป็นภาษาที่ดีมาก มีเป้าหมายชัด และเครื่องมือก็ดี
  • ประเด็นที่ OP ชี้ว่า การแปลง Stream.Reader เป็น std.Io.Reader ต้องเรียกเมธอด interface() แต่ถ้าจะเอา std.io.Writer จาก Stream.Writer กลับต้องใช้ address ของฟิลด์ &interface นั้น ดูไม่สอดคล้องกัน ซึ่งผมคิดว่าถ้าเป็นคอมมูนิตี้ Go คงปัดตกไปตั้งแต่แรก Go มักตัดสินใจแม้เรื่องเล็กน้อยหลังผ่านการวิเคราะห์ที่ลึกมาก ตัวอย่าง issue ของ Go ที่ผมชอบมาก: Go github issue #45624 ถกกัน 4 ปีกว่าจะได้ข้อสรุป มันอาจช้า แต่ก็ตรวจสอบทั้งความสอดคล้อง การออกแบบ และการใช้งานในโค้ดจริงอย่างละเอียด ผมคิดว่ามันช้าเท่าที่จำเป็น และผลลัพธ์ที่ได้ก็มักมีคุณภาพสูงมาก

    • Rust ก็คล้ายกัน มีฟีเจอร์ที่มีประโยชน์มากจำนวนหนึ่งที่อยู่แค่ใน nightly rust และยังไม่เข้า stable (เช่น generator) มันน่าหงุดหงิด แต่ฟีเจอร์ที่เข้า stable จะผ่านการตรวจสอบอย่างลึกมาก ผมเองใจร้อน แต่ก็คิดว่าแนวทางของทีม Rust นั้นเหมาะสม

    • ก่อน Go 1.0 มันไม่ได้ช้าขนาดนั้น ถึงจะไม่ได้เปลี่ยนระดับรากฐานบ่อยนัก แต่ก็มีการเปลี่ยนใหญ่เกิดขึ้นบ่อย (เช่น ตัด semicolon ออก เปลี่ยนประเภท error ฯลฯ) และยังมีเครื่องมือแปลงโค้ดอัตโนมัติให้ด้วย พอถึง 1.0 จึงค่อยให้สัญญาเรื่องเสถียรภาพและกลายเป็นแนวทางแบบทุกวันนี้

  • Zig เป็นภาษาที่ผมนึกถึงเป็นอันดับแรกเวลาจะทำงาน low-level และการที่ใช้ Zig เป็น cross compiler สำหรับ C/C++ ได้ด้วยก็เจ๋งมาก

  • ปัญหาส่วนใหญ่ดูเหมือนเกิดจากเอกสารไม่พอหรือคุณภาพเอกสารไม่ดี

    • เพราะหลายอย่างใน Zig เปลี่ยนบ่อยเกินไป เอกสารเลยดูไม่ใช่สิ่งสำคัญอันดับต้น ๆ แม้แต่ tutorial ของ Zig ก็แทบให้ความรู้สึกเหมือน "รวมโค้ดตัวอย่าง" (และหลายตัวอย่างก็รันกับคอมไพเลอร์รุ่นล่าสุดไม่ได้แล้ว) รวมถึงนิยามใน standard library จำนวนมากก็ต้องไปอ่านซอร์สโค้ดเอง ถ้าคุณเข้าใจลูกเล่นของ syntax ของ Zig หมดแล้ว ฟังก์ชันง่าย ๆ มักจะสั้น มีเหตุผล และชื่อชัดเจน จึงทำให้คนเขียนรู้สึกง่าย แนวคิดเรื่อง Allocators เองก็ไม่ได้ยากในเชิงแนวคิดนัก แค่ไม่ได้อยากเขียนเอง แต่พอเป็นแนวคิดที่ซับซ้อนกว่านั้น ข้อจำกัดก็ชัดเจนมาก ระบบ IO ใหม่ของ Zig ดูเหมือนโครงสร้าง Streams/Readers/Writers ของ Java ที่ห่อหลายชั้น มันพยายามทำให้การเขียน output ง่ายขึ้นด้วยรูปแบบง่าย ๆ อย่าง output.write("hello") แต่ในทางปฏิบัติกลับทำให้คนสับสน เพราะอธิบายวิธีใช้งานไม่พอ และก็ยังสงสัยด้วยว่าจำเป็นไหมที่ standard library ต้องแสดงระบบ type ที่ซับซ้อนขนาดนี้ ทั้งที่ Zig โดยรวมเต็มไปด้วยเมธอดที่ชัด กระชับ และอ่านง่าย แต่ระบบ IO ใหม่นี้กลับห่างไกลจากสิ่งนั้นและไม่เป็นธรรมชาติ
  • (ระบบใหม่ของ zig) มีปัญหาตรงที่เอาแนวคิดที่เดิมมีไว้แค่แบ่งขอบเขตการทำงาน มาผสมเข้ากับ runtime engine ทั้งหมด แต่กลับไม่อธิบายให้ชัดว่าจะเชื่อมสองฝั่งนั้นเข้าหากันอย่างไร