1 điểm bởi GN⁺ 2025-11-08 | 1 bình luận | Chia sẻ qua WhatsApp
  • Trình biên dịch Zig cung cấp sẵn khả năng biên dịch mã C và biên dịch chéo là ngôn ngữ gây kinh ngạc nhất mà tác giả từng trải nghiệm trong 45 năm làm nghề
  • Với những tính năng độc đáo như thực thi tại thời điểm biên dịch, biến có kích thước bit tùy ý, môi trường khối kiểm thử, Zig không chỉ là bản thay thế đơn thuần cho C/C++ mà còn mang đến một cách lập trình hoàn toàn mới
  • Nhờ cú pháp ngắn gọn và rõ ràng như khai báo biến bằng suy luận kiểu, struct ẩn danh, label break, người học có thể tiếp cận rất nhanh
  • Hỗ trợ gỡ lỗi mã tối ưu hóa bằng kiểm thử mô-đun độc lập thông qua khối kiểm thử và hàm dựng sẵn @breakpoint
  • Hỗ trợ lập trình mức thấp bằng bit field và phép toán bit, qua đó đồng thời đạt được hiệu quả lẫn độ vững chắc, đồng thời tích hợp ưu điểm của ngôn ngữ thông dịch vào ngôn ngữ biên dịch

Lời mở đầu

  • Trong 45 năm kinh nghiệm, chưa từng có ngôn ngữ nào gây ấn tượng như Zig
    • Zig không chỉ là một ngôn ngữ mới, mà là công cụ thay đổi tận gốc cách lập trình
  • Chỉ xem Zig ở mức thay thế C hay C++ là đánh giá thấp nghiêm trọng
  • Mục đích của bài viết này là giới thiệu những tính năng đơn giản nhưng hấp dẫn của Zig và giúp lập trình viên có thể bắt đầu nhanh chóng
  • Trong công nghiệp còn có nhiều tính năng khác ảnh hưởng đến mức độ chấp nhận Zig

Trình biên dịch Zig

  • Zig mặc định cung cấp khả năng biên dịch mã C và biên dịch chéo mà không cần cấu hình riêng, tạo ảnh hưởng lớn trong công nghiệp
  • Cài đặt bằng cách tải trình biên dịch tương ứng với bộ xử lý/HĐH từ trang tải xuống của Ziglang, giải nén rồi chép vào thư mục mong muốn
    • Trên Windows 10, có thể chép tệp zip x86_64 vào Program Files, đổi tên thư mục gốc thành zig-windows-x86_64 để khi nâng cấp phiên bản không cần sửa biến môi trường Path
    • Sau khi thêm đường dẫn thư mục gốc vào biến môi trường Path, có thể dùng trình biên dịch ở chế độ CLI
  • Để build chương trình Hello World!, nên tham khảo mục Getting Started trên trang chính thức

Khái niệm và lệnh chính

Khai báo biến

  • Khai báo biến gồm phần đầu là quyền truy cập (pub hoặc lược bỏ), var/const, tên biến; phần hai là khai báo kiểu; phần ba là khởi tạo
    • Chỉ phần đầu và phần ba là bắt buộc, còn kiểu có thể được suy ra từ giá trị khởi tạo
    • Ví dụ: var sum : usize = 0;
  • Biến khai báo không có pub chỉ truy cập được trong mô-đun (tương tự biến static trong C)
  • Không khuyến nghị khai báo biến pub; nên giảm thiểu hàm pub để hạ độ kết dính giữa các thành phần và tăng tính gắn kết nội bộ

Struct, struct ẩn danh, khối kiểm thử

  • Literal struct ẩn danh được bao bởi .{} được dùng để khởi tạo phần tử của struct khác hoặc tạo struct mới đã khởi tạo phần tử
  • .{ } là literal struct ẩn danh rỗng
  • Dạng struct { } là khai báo struct
  • Khối kiểm thử cho phép biên dịch và chạy kiểm thử mà không cần file thực thi

Bit field

  • Bit field được khai báo trong packed struct dưới dạng trường có kiểu với kích thước cụ thể
  • Con trỏ có thể trỏ tới một bit field cụ thể

Vòng lặp for

  • Cú pháp Zig rõ ràng hơn C, nhưng dùng khoảng mở [0..9) thay vì [0..8]
  • Việc khai báo kiểu, khởi tạo, kiểm tra và tăng biến lặp i đều được xử lý tự động

Mảng

  • [_] định nghĩa một mảng chưa biết kích thước, sau đó là kiểu phần tử và phần khởi tạo
    • Ví dụ: var grid = [_]u8{0} ** 81; khởi tạo 81 phần tử u8 bằng 0
    • Kích thước mảng được suy ra từ đối số lặp trong khởi tạo
  • Trong môi trường kiểm thử, có thể duyệt các phần tử mảng và tính tổng
  • Biến được khai báo giữa dấu | của vòng for mặc định được xem là cùng kiểu với phần tử mảng
  • usize là số nguyên không dấu tự nhiên của nền tảng (u64 trên 64-bit, u32 trên 32-bit)

Con trỏ nhiều phần tử

  • Nếu con trỏ mảng muốn dùng phép toán số học trên con trỏ thì phải khai báo tường minh là con trỏ nhiều phần tử như [*]const i32
  • Dù mảng là const, con trỏ vẫn có thể được khai báo là var

Giải tham chiếu con trỏ

  • Con trỏ được gán địa chỉ của một vị trí mảng riêng lẻ thì không thể cập nhật bằng số học con trỏ
  • Giải tham chiếu con trỏ dùng ptr.*

Label break

  • Có thể thực hiện nhiều công việc khác nhau tại thời điểm biên dịch, như khởi tạo mảng
  • Label break đặt : sau tên khối, rồi dùng break để trả về giá trị từ khối
    • Ví dụ: break :init m;
  • 0.. là phạm vi vô hạn bắt đầu từ 0
  • Trong vòng for, các biến được tự động khởi tạo và tăng; vòng lặp dừng sau khi xử lý vị trí cuối của mảng
  • Mảng có thể không cần khởi tạo tường minh bằng undefined

Hàm trong Zig

  • Hàm được khai báo bằng fn và mặc định là static (chỉ dùng trong file)
    • Nếu khai báo pub fn thì có thể được import từ file khác
  • Hàm có thể được "inlined"
  • Con trỏ hàm có const ở trước và prototype hàm ở sau

Lập trình hướng đối tượng trong Zig

  • Struct có thể chứa hàm
  • Trong ví dụ stack, có thể lưu tối đa 81 phần tử (kiểu StkNode)
  • Zig không có toán tử ++--; thay vào đó dùng +=-=
  • Con trỏ stack là một số nguyên dùng làm chỉ mục của mảng stk
  • Con trỏ self không được truyền tường minh như tham số; nó được ngầm hiểu gián tiếp là con trỏ tới thể hiện stack đang gọi hàm
    • Khi gọi như stack.pop(), self là con trỏ tới stack (tương tự this trong Java/C++)
  • Hàm init() là constructor của stack
  • Các hàm poppush được "inlined"

Build và chạy chương trình Zig

Build file thực thi

  • Để tạo file thực thi cần có hàm main biểu thị điểm vào chương trình
  • Chương trình đơn giản có thể đặt hàm main trong cùng file
  • Để gỡ lỗi mô-đun độc lập, có thể chèn hàm main ở cuối file rồi comment lại sau khi debug xong
  • Lệnh biên dịch: zig build-exe -O ReleaseFast program.zig

Chạy khối kiểm thử của mô-đun

  • Đây là một trong những tính năng xuất sắc nhất của Zig, dùng cho kiểm thử và tạo prototype
  • Khối kiểm thử bắt đầu bằng test "message" { và kết thúc bằng }
    • message là chuỗi được hiển thị khi chạy kiểm thử
  • Khối kiểm thử chạy độc lập với file thực thi; file thực thi cuối cùng sẽ không chạy các kiểm thử này
  • Lệnh kiểm thử: zig test module.zig
  • Khối kiểm thử trong example.zig kiểm tra các hàm setprint; set nhận chuỗi thập phân làm tham số, còn print in tiêu đề Input Grid rồi in ra grid

Xuất dữ liệu trong Zig

  • Câu lệnh std.debug.print gọi hàm print trong debug.zig của thư viện chuẩn Zig std
  • Tham số đầu tiên là chuỗi định dạng, tham số thứ hai là struct ẩn danh chứa danh sách biến cần hiển thị
  • Nếu không có định dạng thì struct sẽ rỗng
  • Mặc định hiển thị ra stderr
  • Khác với printf của C, Zig có thể xử lý chuỗi literal và danh sách biến tại thời điểm biên dịch

Gỡ lỗi file thực thi

  • Việc dùng debugger không đơn giản nếu không có IDE tích hợp debugger như Eclipse, IntelliJ IDEA hoặc bộ công cụ phát triển tích hợp như w64devkit
  • Tích hợp symbol làm mã phình to và yêu cầu biên dịch ở chế độ Debug, dẫn đến mã thực thi kém hiệu quả đáng kể
  • Zig cung cấp một cách giải quyết tiện lợi để tránh các vấn đề đó

Hàm dựng sẵn @breakpoint

  • Chèn @breakpoint(); vào mã nguồn để khi chạy trong debugger, chương trình sẽ dừng tại đúng điểm đó
  • Đây là tính năng hữu ích để gỡ lỗi mã Zig đã tối ưu hóa mà không cần symbol
  • Ngay trước @breakpoint();, có thể dùng std.debug.print để in các biến cần theo dõi và xem giá trị của chúng tại thời điểm đó
  • Trong ví dụ debug_example.zig, tác giả chèn mã in grid cùng các biến bên trong hàm set và thêm @breakpoint();
  • Lệnh build: zig build-exe debug_example.zig
  • Gọi debug_example.exe bằng debugger như gdb rồi dùng lệnh r để chạy chương trình
  • Dùng lệnh c để tiếp tục và theo dõi nội dung grid cùng các biến
  • Nếu tiếp tục nhấn Enter để chạy tiếp, có thể xác nhận rằng các giá trị trong grid khớp với khối kiểm thử của example.zig

Lập trình mức thấp trong Zig

Biểu diễn ma trận

  • Các chữ số thập phân được lưu trong ma trận bằng số nguyên chuẩn u8
  • grid đầu vào ở dạng chuỗi, nhưng ký tự ASCII sẽ được chuyển nội bộ thành số nguyên u8
  • Việc lưu trữ số được tổ chức tuyến tính theo từng dòng trong mảng grid gồm 81 vị trí: var grid = [_]u8{0} ** 81;
  • Để kiểm tra tính đúng đắn của grid, cần truy cập các phần tử theo từng dòng và cột
  • Tạo một mảng gồm 9 con trỏ, mỗi con trỏ trỏ tới đầu của một dòng
  • Dùng label break để trả về giá trị từ một khối mã: break :fill9x9 m; để khởi tạo matrix bằng m
  • Cú pháp truy cập phần tử: element = matrix[i][j]

Biểu diễn chữ số thập phân bằng bit

  • Khái niệm cốt lõi là thay chữ số thập phân nguyên i bằng số nguyên code
    • i ∈ [1,9] → code = 2ⁱ⁻¹
    • i = 0 → code = 0
  • Vị trí bit duy nhất được đặt thành 1 trong codei-1 (khi i nằm trong khoảng 1~9), nếu không thì mọi bit đều bằng 0
  • Bài viết cung cấp bảng giá trị code cho từng số (1→1, 2→2, 3→4, ..., 9→256)

Tính code trong Zig

  • Chỉ khi c khác 0 mới tính giá trị code bằng toán tử dịch trái: code = @as(u9,1) << (c-1);
  • Trong Zig, hằng số cần có kích thước phù hợp để phép toán được biên dịch và kết quả được gán cho biến
  • code được khai báo là kiểu u9 (vì giá trị lớn nhất 256 cần ít nhất 9 bit)
  • Zig cho phép dùng biến có kích thước bit tùy ý
  • Dùng hàm dựng sẵn @as để ép kiểu hằng 1 sang u9

Biểu diễn grid bằng bit field

Grid bit field theo dòng

  • Mảng lines phản chiếu toàn bộ grid, biểu diễn mỗi dòng bằng một số nguyên 9 bit: var lines = [_]u9{0} ** 9;
  • Khi truy cập dòng i, có thể kiểm tra một chữ số cụ thể đã tồn tại trong dòng đó chưa bằng phép AND bit (&): lines[i] & code
  • Nếu kết quả là 0 thì số đó chưa có trong dòng i, ngược lại là bị trùng

Grid bit field theo cột

  • Mảng columns phản chiếu toàn bộ grid, biểu diễn mỗi cột bằng một số nguyên 9 bit: var columns = [_]u9{0} ** 9;
  • Khi truy cập cột j, có thể kiểm tra một chữ số cụ thể đã tồn tại trong cột đó chưa bằng phép AND bit: columns[j] & code
  • Nếu kết quả là 0 thì số đó chưa có trong cột j, ngược lại là bị trùng

Quy tắc Sudoku

  • Khi chèn một số mới vào grid Sudoku rỗng, nó không được tồn tại sẵn trong toàn bộ dòng, cột và ô chứa phần tử mới đó
  • Ô ở đây là một trong 9 vùng lưới 3x3 được ngăn bởi các đường đậm
  • Mỗi phần tử cụ thể trong lưới 9x9 thuộc về đúng một dòng, một cột và một ô
  • Trong lưới ví dụ, ô đầu tiên chứa 3, 5, 6, 8, 9 và còn thiếu 1, 2, 4, 7
  • Các mảng linescolumns xử lý việc kiểm tra trùng lặp theo dòng và cột
  • Cần thêm một mảng mới để kiểm tra trùng lặp theo ô

Grid bit field theo ô

  • Mảng cells phản chiếu toàn bộ grid, biểu diễn mỗi ô bằng một số nguyên 9 bit: var cells = [_]u9{0} ** 9;
  • Sẽ dễ hơn nếu truy cập cells như một ma trận 3x3
  • Điền mảng cell tương tự như cách đã làm với ma trận 9x9
  • Cần xác định dòng và cột của ma trận cell từ dòng và cột của phần tử trong grid 9x9 gốc
  • Vì phép chia số nguyên rất chậm, dùng mảng cindx = [_]usize{ 0,0,0, 1,1,1, 2,2,2 }; để cung cấp kết quả chia
  • Khi truy cập ma trận bằng dòng i và cột j của phần tử trong lưới 9x9, có thể kiểm tra một chữ số cụ thể đã tồn tại trong ô của phần tử đó chưa bằng phép AND bit: cell[cindx[i]][cindx[j]] & code
  • Nếu kết quả là 0 thì số đó chưa có trong ô, ngược lại là bị trùng

Kiểm tra trùng phần tử

  • Có thể hoàn tất việc kiểm tra trùng lặp bằng cách OR bit (|) tất cả các phần tử trước đó trong cùng dòng, cột và ô, rồi AND bit với code của phần tử
if (((lines[i]|columns[j]|cell[cindx[i]][cindx[j]])&code) != 0) {  
    unreachable;  
}  
  • Nếu kết quả là 0, phần tử đó chưa tồn tại trong dòng, cột hoặc ô
  • Nếu kết quả khác 0, chương trình sẽ dừng bằng lệnh unreachable
  • Đây là cách đơn giản nhất trong Zig để biểu thị tường minh lỗi thực thi
  • Mã thực tế còn in ra chi tiết vị trí phát sinh lỗi
  • Ví dụ: nếu thay ký tự 0 ngay sau ký tự 8 đầu tiên trong chuỗi đầu vào bằng 5, sẽ phát sinh lỗi vì ở dòng 3 cột 1 đã có số 5

Cập nhật cấu trúc dữ liệu

  • Trong hàm set, vòng for lồng nhau tương tác theo từng dòng để chép từng phần tử mới từ chuỗi đầu vào s vào grid
    • Biến k giữ chỉ số của ký tự đầu vào mới trong chuỗi s
  • Ký tự được chuyển thành u4 (biến c) bằng cách trừ '0'
  • Nếu phần tử mới chèn vào grid khác 0 (c != 0), code được tính bằng lệnh dịch trái sẽ được chép vào từng lưới phản chiếu
    • Thực hiện OR bit với lưới phản chiếu tương ứng (|=):
lines[i] |= code;  
columns[j] |= code;  
cell[cindx[i]][cindx[j]] |= code;  
  • Không cần kiểm tra tường minh xem c có nằm trong khoảng 1~9 hay không, vì phép dịch sẽ gây tràn khi thực thi nếu giá trị không hợp lệ
  • Ví dụ: nếu thay ký tự 0 ngay sau ký tự 8 đầu tiên trong chuỗi đầu vào bằng :, sẽ xảy ra lỗi thực thi
  • Thay cùng ký tự 0 đó bằng / cũng gây lỗi thực thi tương tự
  • Chương trình chỉ hoạt động khi giá trị nằm trong khoảng 1~9, tức grid đầu vào chỉ chứa chữ số thập phân
  • Nhiều lưới Sudoku trên web dùng . thay cho 0, nên trong hàm set có dòng if (s[k] == '.') c = 0;
  • Cách này giúp bỏ qua thuận tiện phép dịch bit vì c khi đó bằng 0

Tạo prototype và độ vững chắc

  • Hai phần trước minh họa các lỗi cưỡng bức như một tính năng quan trọng của Zig
  • Một mặt là độ vững chắc của Zig — với phép dịch bit, hành vi sai không được phép tồn tại và sẽ bị bắt ở thời gian chạy
  • Dù mọi nỗ lực có vẻ đều hướng tới hiệu quả, đây lại là trường hợp điển hình khi hiệu năng được đánh đổi với độ vững chắc
  • Trong C, nếu phép dịch làm mất bit thì đó là vấn đề của lập trình viên, đổi lại có hiệu năng tốt hơn từ một số lệnh assembler cụ thể
  • Tính năng còn lại là khả năng dùng khối kiểm thử để tạo prototype
  • Khả năng ứng dụng là vô số, và ví dụ được trình bày chỉ là gỡ lỗi một tình huống cụ thể khi phát sinh lỗi
  • Chỉ riêng những tính năng này cũng đã mang lại năng lực đáng kinh ngạc vốn rất hiếm thấy ở ngôn ngữ lập trình, đặc biệt là ngôn ngữ biên dịch

Kết luận

  • Zig được cấu thành từ ba yếu tố cốt lõi: tương thích C, biên dịch chéo, và cài đặt đơn giản
  • Những đặc tính này cho thấy tiềm năng của Zig trong việc trở thành chuẩn mực mới cho ngôn ngữ lập trình hệ thống
  • Nhiều ưu điểm vốn chỉ có ở ngôn ngữ thông dịch đang dần chuyển sang ngôn ngữ biên dịch để mang lại hiệu năng tốt hơn
  • Zig đặc biệt nổi bật về sự tương đồng với ngôn ngữ thông dịch nhờ khái niệm thực thi tại thời điểm biên dịch
  • Điều đó vừa khiến Zig trở nên khác biệt và mạnh mẽ, vừa khiến nó khó nắm bắt hơn

1 bình luận

 
GN⁺ 2025-11-08
Ý kiến Hacker News
  • Bài viết mở đầu bằng việc cho rằng “Zig không chỉ là một ngôn ngữ đơn giản mà là một cách lập trình hoàn toàn mới”, nhưng thực tế lại hầu như không đề cập đến các tính năng thực sự đặc trưng của Zig
    Suy luận kiểu, struct ẩn danh, labeled break... đều đã tồn tại từ rất lâu trong các ngôn ngữ khác
    Thứ thực sự độc đáo là comptime, nhưng phần này lại không được nhắc đến chút nào
    Dù không phải là khái niệm hoàn toàn mới như macro của Lisp, cách Zig dùng nó thay cho generic khá thú vị
    Tuy vậy, lập luận của bài viết tạo cảm giác cường điệu hóa khá nhiều

    • Rust cũng có thể được xem là “một cách hoàn toàn mới” theo nghĩa tương tự
      Rust cho phép biểu đạt rõ ràng thời điểm mã được thực thi, và thiết kế kiểu giống một query engine để duyệt toàn bộ không gian mã nguồn rất ấn tượng
    • Ngôn ngữ D đã hỗ trợ thực thi hàm tại thời điểm biên dịch từ năm 2007
      Xem tài liệu D
      Nếu là const-expression thì nó sẽ tự động được thực thi
    • Giờ đây việc gộp C/C++ thành một nhóm đã không còn nhiều ý nghĩa
      Chúng khác nhau như Java và Scala vậy
    • comptime không phải là một phát minh mang tính phép màu mà là phiên bản hiện đại của metaprogramming
      Zig gọn gàng hơn template của C++, nhưng tạo cảm giác là một lựa chọn thay thế thực dụng hơn là một cuộc cách mạng
      Cá nhân tôi không hiểu được sự cuồng nhiệt quá mức dành cho nó, giống như thời Rust trước đây
    • Khi thấy cụm “một cách hoàn toàn mới”, tôi đã kỳ vọng vào một paradigm mới như LISP hay Prolog, nhưng thực tế không phải vậy
      Tôi đã đọc hết tài liệu Zig mà vẫn không thấy điều gì đáng kinh ngạc, nên khá bối rối
  • Vấn đề lớn nhất của Zig là không thể gắn dữ liệu vào error
    Error chỉ được truyền qua một kênh phụ, khiến việc debug khó khăn, và rốt cuộc các lập trình viên thường bỏ qua dữ liệu lỗi
    Xem issue liên quan
    Chỉ với những mã đơn giản như AccessDenied thì rất khó biết nguyên nhân là gì

    • Tôi đã đọc bài viết của matklad, và thấy cách tiếp cận tách riêng mã lỗi với thông tin chẩn đoán khá thuyết phục
      Trên thực tế, ngay cả khi dùng một đối tượng Error phức tạp thì nhiều khi vẫn cần một kênh chẩn đoán riêng
    • Trong ngôn ngữ hệ thống, việc gắn dữ liệu vào error không phải lúc nào cũng là ý hay
      chi phí hiệu năng hoặc vấn đề trạng thái hệ thống, trong nhiều trường hợp xử lý bằng delayed binding sẽ an toàn hơn
      Zig theo đuổi triết lý ưu tiên độ chính xác và tính quyết định kiểu này
    • Trong Zig cũng đang có thảo luận về việc đưa thông tin do người dùng định nghĩa vào stack trace của error
      Xem issue liên quan
      Nhưng điều thực sự cần là structured logging và khả năng theo dõi ngữ cảnh dựa trên call stack
    • std.zon thường được nêu như một ví dụ tốt, và trong cộng đồng cũng đang có xu hướng tập hợp nhiều mẫu xử lý lỗi khác nhau để phản ánh vào tiêu chuẩn
    • Việc không cho phép gắn dữ liệu vào error ngược lại còn thúc đẩy thiết kế lỗi rõ ràng
      Nó có thể ngăn những lập trình viên lười biếng bừa bãi nhồi dữ liệu vào mọi thứ
  • Tôi đồng ý với nhận định rằng “cách phát triển Zig tự thân nó là một cách phát triển ngôn ngữ mới”
    Quá trình tiến hóa chậm rãi với việc cân nhắc kỹ tính năng và loại bỏ những thứ không cần thiết khá ấn tượng

    • Nhưng cách tiếp cận này cũng rất phổ biến ở Java, Rust và nhiều ngôn ngữ khác
      Tôi muốn nghe cụ thể hơn đâu là điểm thật sự độc đáo của Zig
  • Tôi thích việc có thể cài Zig bằng PyPI
    Chỉ cần cài gói ziglang bằng pip install ziglang là có thể dùng ngay
    Cũng có thể dùng uvx để build mã C

    • Có thể cài theo cách này vì Python wheel cho phép bundle phần mềm tùy ý
    • Nhưng cách tiếp cận này lại cho cảm giác như một sự tái phát minh kém tiện hơn nix
    • Tôi ước Nim cũng có tùy chọn cài đặt kiểu này
    • Cá nhân tôi thấy micromamba hay pixi là cách quản lý gói tốt hơn pip/uv
    • Nhờ các công cụ AI, giờ việc học bất kỳ ngôn ngữ nào cũng dễ hơn rất nhiều
  • Thật đáng tiếc khi những tính năng vốn đã tồn tại từ lâu trong các ngôn ngữ như Ada, Object Pascal, Modula-2 lại được đóng gói thành “đổi mới” của Zig
    Thật thú vị khi các ý tưởng từ 40 năm trước lại trông mới mẻ chỉ vì được bọc lại bằng cú pháp kiểu C

  • Phần mở đầu của bài viết khá ổn, nhưng sau đó chỉ dừng ở việc liệt kê các tính năng của Zig
    Cú pháp trực quanluồng điều khiển tường minh của Zig (defer chẳng hạn) rất hấp dẫn
    Nhờ comptime nên cũng không cần học thêm cú pháp macro riêng biệt

    • Sức hút thực sự của Zig là thiết kế không có sự trùng lặp không cần thiết
      Mọi thành phần đều ăn khớp tự nhiên đến mức dù mới dùng cũng có cảm giác như một công cụ đã gắn bó từ lâu
    • Bài phân tích cú pháp Zig của matklad cũng đáng tham khảo
  • Cú pháp for (0..9) của Zig khá trực quan, nhưng vì là khoảng mở nên đôi khi dễ gây nhầm lẫn
    Giống như range(0, 9) của Python, rất dễ quên liệu giá trị cuối có được bao gồm hay không

    • Rust phân biệt rõ bằng 0..90..=9 nên minh bạch hơn
    • Sự nhất quán của Zig khi chỉ dùng khoảng nửa mở lại giúp giảm lỗi
      Kích thước khoảng có thể tính đơn giản bằng hiệu số, và việc duyệt ngược cũng trở nên dễ hơn
    • Odin phân biệt tường minh hơn bằng 0..<5 (mở) và 0...5 (đóng)
  • Tôi không thích quy tắc định danh của Zig
    Việc trộn lẫn snake_case và camelCase tạo cảm giác hơi kỳ
    Dù vậy, hệ thống build, allocator bộ nhớ và trải nghiệm biên dịch của nó đều rất xuất sắc
    Tôi chủ yếu dùng Rust, nhưng vẫn luôn tò mò về Zig

    • Tôi cũng vậy. Cá nhân tôi không tuân theo quy ước đặt tên cho private function
      Quy ước tiền tố của thư viện C cũng phiền tương tự
  • Sức hút của Zig không nằm ở một tính năng đơn lẻ nào, mà ở sự tích lũy của các quyết định thực dụng
    Những lựa chọn ban đầu trông có vẻ cấp tiến, nhưng càng hiểu sâu lại càng thấy hợp lý
    Zig là một ngôn ngữ tưởng thưởng cho những lập trình viên hiếu kỳ

    • Tôi đã thử làm một trò chơi nhỏ bằng Odin, và đó thực sự là một trải nghiệm rất thú vị
  • Một trong những lý do Zig được đánh giá cao là vì nó thừa nhận thực tế của mã hệ thống cấp thấp
    Nhiều ngôn ngữ né tránh phần này vì lý do thẩm mỹ, nhưng Zig thì không

    • Nếu chuyển sang phần định nghĩa của thư viện chuẩn, bạn còn có thể trực tiếp thấy cả việc xử lý các trường hợp đặc biệt như Plan9 OS
      Xem tài liệu page_allocator
    • Tuy vậy, vẫn cần thêm ví dụ cụ thể để củng cố nhận định này