- ใน 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 การทำความเข้าใจข้อกำหนดยิ่งรู้สึกยากขึ้นไปอีก
- โดยสรุปแล้ว เมื่อเทียบกับเดิม ยังมีหลายส่วนที่ ขาดความชัดเจน การจัดทำเอกสาร และความสะดวกในการใช้งาน
ยังไม่มีความคิดเห็น