Zig（也稱為 Ziglang） 是一種 命令式、通用、靜態型別、編譯語言的 系統 程式語言，由 Andrew Kelley 設計。 它旨在作為 C 的繼任者，提供更輕量更簡單的程式設計體驗，同時提供更豐富的功能。 它是 自由及開放原始碼軟體，在 MIT 許可證 下發布。 Zig 語言的簡化涉及到流控制、函式呼叫、庫匯入、變數宣告和 Unicode 支援。然而，Zig 不使用 宏 或 預處理 指令。Zig 從現代語言引入的特性包括 編譯時泛型程式設計資料型別，允許函式編譯時處理各種資料，並添加了一小套新的 編譯器 指令，以允許透過 反射程式設計 訪問和修改這些型別的資訊。 Zig 的另一組新增功能旨在提高程式碼安全性。像 C 一樣，Zig 沒有實現 垃圾回收，其 記憶體管理 是手動的。對於包括此種情況的錯誤處理，Zig 引入了 可選型別，他們的 語法 很簡單，而 Zig 內置於語言中的 單元測試 框架也使提前預知錯誤變得簡單和方便。 描述 目標 Zig 的主要目標是成為解決目前由 C 解決的各種任務的更好的解決方案。對此的主要關注點是可讀性；Zig 儘可能使用現有概念和語法，避免為相似概念新增不同的語法。此外，它的設計目標是 穩健性、最佳化和可維護性，包括各種提高安全性、最佳化和測試的功能。簡潔的語法是維護的重要組成部分，因為該語言的目標是允許維護人員在不需要學習他們可能不熟悉的語言細節的情況下除錯程式碼。 即使有這些更改，Zig 也可以編譯並與現有的 C 程式碼一起使用；可以在 Zig 專案中包含 C 標頭檔案並呼叫它們的函式，並且，透過包含編譯器生成的標頭檔案，Zig 程式碼可以被連結到 C 專案。 為了保持整體設計理念的簡潔和易讀，Zig 系統整體相比於 C 及其他類似 C 的語言也包含了一些風格上的變化。例如，Rust 語言具有 運算子過載，這意味著類似 的語句實際上可能是對型別的過載版本的加運算子的函式呼叫。此外，該函式可能會引發 panic，從而可能中斷任何後續程式碼。在 Zig 中，如果某個東西呼叫了一個函式，那麼它看起來就像一個函式呼叫；如果沒有呼叫，它看起來就不會像一個函式呼叫。如果它可能丟擲錯誤，則一定會在程式碼中顯式表示它可能丟擲錯誤， 而錯誤處理可以透過錯誤型別處理，也可以透過 catch 或 try 處理。 Zig 的目標與同時期設計的許多其他語言（如 Go、Rust、Carbon 和 Nim）的目標形成對比。通常，這些語言更復雜，他們添加了運算子過載、看起來像值（屬性）呼叫的函式等許多功能，這些旨在幫助構建大型程式。並且這些特性更接近於 C 的特性，而這些語言也更像 C。 Zig 在型別系統的擴充套件上更為保守，支援編譯時泛型，並透過 特性實現了一種 鴨子型別 形式的用法。 記憶體處理 C 程式中 錯誤 的主要來源之一是基於 malloc 的 記憶體管理 系統。malloc 為程式碼使用分配一塊記憶體，並返回該記憶體的引用作為一個 指標。但沒有一套系統來確保在程式不再需要記憶體時釋放該記憶體，這可能導致 記憶體洩漏以致於耗費完所有的可用記憶體。更常見的是 懸空指標，它不引用一個正確分配的記憶體物件。 解決這些問題的常見方法是 垃圾回收（GC），它會檢查程式中指向先前分配過記憶體的指標，並刪除所有的不再有指標指向它們的記憶體塊。儘管這在很大程度上減少甚至消除了記憶體錯誤，但 GC 系統的速度相對較慢，，並且具有不可預測的效能，使其不適用於 系統程式設計。另一種解決方案是 自動引用計數（ARC），它透過維護指向一個塊的指標數，在指標建立和銷燬時來實現相同的基本功能，這意味著不需要執行詳盡的指標搜尋，而僅增加每個指標在執行建立和銷燬操作時引用計數器的開銷。 Zig 旨在提供與 C 相似或更好的效能，因此 GC 和 ARC 不是合適的解決方案。相反，它使用一種現代的（截至 2022 年），被稱為 可選型別 的概念。與指標可以指向空值或 nil 不同，使用一個單獨的型別來指示可能為空的資料。這類似於使用一個指標和一個布林值的結構來指示指標是否有效，但布林值的狀態由語言隱式管理，不需要程式設計師顯式管理。因此，例如，當宣告指標時，它被設定為 未分配，而當該指標從 malloc 接收一個值時，如果 malloc 成功，它將被設定為 已分配。 這種模型的優點是它具有非常低甚至是零的開銷；儘管這使得編譯器必須建立在操作指標時傳遞可選型別，而不是一個簡單的指標的程式碼，但這允許它在編譯時直接檢查出可能的記憶體問題，而無需延後到執行時檢查。例如，在 C 中建立一個具有空值的指標並嘗試使用它是完全可以接受的，儘管這會導致空指標錯誤。相比之下，使用可選型別的語言可以保證所有程式碼只會在指標有效時嘗試使用相應的指標。雖然這不能消除所有潛在問題，但當在執行時發生問題時，錯誤可以被更精確地定位和解釋。 Zig 中記憶體管理的另一變化是：實際分配操作是透過 描述相應的操作來處理的，而不是直接呼叫 libc 中的記憶體管理函式。例如，在 C 中，如果想寫一個包含多個副本的字串的函式，該函式可能如下所示： const char repeat const char original size_t times 在程式碼中，該函式會檢查 的大小，然後 malloc 的記憶體來為它將構建的字串預留空間。但這個 malloc 對呼叫它的函式是不可見的，所以，如果像 repeat 這樣的函式沒有正常釋放記憶體，就會發生記憶體洩漏。而在 Zig 中，這種操作可能透過如下函式處理： fn repeat allocator stdmemAllocator original const u8 times usize stdmemAllocatorError const u8 在此程式碼中， 變數傳遞了一個記憶體分配器，並且 函式則返回結果字串或 AllocatorError。透過直接將分配器作為輸入傳參，使得記憶體分配不會被 隱藏 起來，即呼叫鏈上的所有記憶體分配都會透過這個分配介面進行分配。順帶一提，Zig 的標準庫內沒有進行任何隱式記憶體分配。此外，由於結構可以指向任何東西，可以使用替代分配器，甚至是程式中編寫的分配器。這使不使用通常分配整個記憶體頁的作業系統函式的小物件分配器等操作成為了可能。 可選型別是一個提供通用功能但仍然簡單和通用的語言特性。它不僅可以用於解決空指標問題，還可以明確地描述 無值 場景。考慮一個名為 的函式，它返回一個整數，以及一個整數變數，，它儲存結果。在許多語言中，會在 中放置一個 魔術數字 以表示 尚未被呼叫，而許多實現則會將其設定為零。在 Zig 中，這可以實現為 ，將變數設定為明確的 未被呼叫 的值。 Zig 另一個有助於管理記憶體問題的更通用特性是 的概念，由它標記的程式碼在函式結束時無論如何都會被執行，包括可能的執行時錯誤。如果某個特定函式分配了一些記憶體，然後在操作完成時釋放它，可以新增一行程式碼，推遲一個 ，以確保無論發生什麼都會釋放它。 但需要注意的是， 並不完全等價於 RAII，在某些場景下，會有許多區別之處。 Zig 記憶體管理避免了隱式分配。即分配不直接由語言本身進行管理。相反，使用者需要透過 標準庫 明確地進行堆訪問來進行顯式記憶體管理。 與 C 的直接互動 Zig 提倡一種將新 Zig 程式碼與現有 C 程式碼結合的漸進方法。為此，它可以儘可能無縫地與現有 C 庫進行互動。Zig 使用 指令匯入庫，通常如下所示： const std import std 如此便可以呼叫 std 內的函式，例如： stddebugprint Hello worldn 要載入 C 程式碼，只需將 替換為 ： const c cImport cInclude soundiosoundioh 然後我們就可以像呼叫本地 Zig 程式碼一樣呼叫 soundio 庫中的函式。由於 Zig 使用新資料型別，它們是顯式定義的，不像 C 的更通用的 和 ，我們需要使用少量指令在 C 和 Zig 型別之間移動資料，包括 和 。 交叉編譯 Zig 將交叉編譯視為語言的一級用例。這意味著任何 Zig 編譯器都可以為其目標平臺之一生成可執行的二進位制檔案，這些平臺包括數十種。這些不僅包括廣泛使用的現代系統，如 ARM 和 x8664，還包括 PowerPC、SPARC、MIPS、RISCV 甚至 IBM 的 zArchitectures S390。工具鏈可以編譯到這些目標中的任何一個，而無需安裝額外的軟體，因為所有需要的支援都在基本系統中。 編譯時計算 透過使用 關鍵字，程式設計師可以顯式地在 編譯時 而不是 執行時 評估程式碼段。能夠在編譯時執行程式碼使得 Zig 具有 宏 和 條件編譯 的功能，而無需單獨的 預處理器 語言。 在編譯時，型別成為 一級公民。這使得在編譯時 鴨子型別 成為可能，這也是 Zig 實現泛型型別的方式。 例如，在 Zig 中，一個泛型的 連結串列 型別可以使用如下函式實現： fn LinkedList comptime T type type 這個函式接收某種型別 ，並返回一個自定義的 ，定義了包含該資料型別的連結串列。 名稱的由來 據報道，Zig 這個名字是透過一個涉及 Python 指令碼的過程選出的，該指令碼隨機組合字母，從字母 Z 開始，然後跟隨一個母音或 Y，以生成四個字母的單詞。儘管目標長度是四個字母，但在生成的各種組合中，最終選擇了三個字母的單詞 Zig。 其他特性 Zig 支援 編譯時 泛型程式設計、反射程式設計 和評估、交叉編譯 以及 手動記憶體管理。 該語言的一個主要目標是改進 C 語言， 同時也從 Rust 等語言中汲取靈感， 等等。Zig 具有許多用於 低階程式語言 的特性，特別是緊湊結構（沒有欄位之間的填充的結構）、任意寬度的整數 和多種指標型別。 Zig 不僅僅是一種新語言：它還包括一個 CC 編譯器，可以與這兩種語言一起使用。 缺點 Zig 有一些缺點。如果記憶體沒有正確釋放，由於缺乏隱式控制，可能會因為忘記 defer 等必要的手動操作導致記憶體洩漏。 對於那些不熟悉低階程式設計概念的人來說，學習 Zig 的曲線可能很陡峭。 儘管 Zig 社群在不斷壯大，但截至 2024 年，它仍然是一個新的語言，在成熟度、生態系統和工具方面有待改進。 與其他語言的互操作性可能會帶來挑戰，因為這通常需要額外的努力來管理資料編組和通訊。 最後，複雜用例的學習資源有限，儘管隨著興趣和採用的增加，這種情況正在逐漸改善。 版本 自 010 版本以來，（新的預設）Zig 編譯器是用 Zig 程式語言編寫的，即它是一個 自舉編譯器，這是該版本的一個重大新特性。舊的遺留 自舉 編譯器是用 C 編寫的，仍然是一個選項，但在 011 版本中將不再是選項。當使用新的 Zig 編譯器進行編譯時，使用的記憶體要少得多，並且編譯速度稍快。舊的、現在的遺留 C 編譯器使用了 35 倍的記憶體。 Zig 的預設最佳化後端仍然是 LLVM，New LLVM version 15 Zig legacy uses version 13 並且 LLVM 是用 C 編寫的。帶有 LLVM 的 Zig 編譯器是 169 MiB，而不帶 LLVM 的則是 44 MiB。通常情況下，使用新的基於 Zig 語言的編譯器編譯的可執行程式碼更快，其 LLVM 程式碼生成更好，並修復了許多錯誤，但在 010 版本中也對舊的遺留編譯器進行了改進。自託管連結器與自託管編譯器緊密耦合。新版本還增加了一些對 AMD GPU 的實驗性（第 3 層）支援（原始碼中還有對 Nvidia GPU 和 PlayStation 4 和 5 的一些較少支援）。 舊的 自舉 stage1 編譯器是用 Zig 和 C 編寫的，使用 LLVM 13 作為後端， 支援其許多本機目標。 編譯器也是 自由及開放原始碼軟體，在 MIT 許可證 下發布。 Zig 編譯器提供了類似於 Clang 的編譯 C 和 C 的能力，命令為 zig cc 和 zig c， 提供了包括 C 標準庫 libc 和 C 標準庫 libcxx 在內的許多不同平臺的標頭檔案，允許 Zig 的 cc 和 c 子命令作為開箱即用的 交叉編譯器。 此外，官方支援（並記錄）的作業系統（主要是桌面作業系統）上可以製作（最小）應用程式，這些應用程式也可以為 Android（使用 Android NDK）和 iOS 程式設計。 在 0110 版本之前，Zig 沒有 包管理器，但在 0110 版本中釋出了一個實驗版本，在 0120 版本中進一步擴充套件。沒有官方的 包倉庫；相反，包只是一個指向 壓縮檔案庫 的 URL，當解壓縮時，包括一個標準的 buildzig 檔案（Zig 編譯器按慣例使用該檔案來編譯原始碼），理想情況下，還包括一個 buildzigzon 檔案，用於定義包的名稱和版本。 Zig 的開發由 Zig 軟體基金會（ZSF）資助，ZSF 是一個由 Andrew Kelley 擔任總裁的非營利公司，接受捐贈並僱傭多名全職員工。 示例 Hello World const std import std pub fn main void const stdout stdiogetStdOut writer try stdoutprint Hello sn world 泛型連結串列 const std import std const stdout stdiogetStdOut writer fn LinkedList comptime T type type return struct const Self This pub const Node struct next Node null data T first Node null pub fn prepend list Self new_node Node void new_nodenext listfirst listfirst new_node pub fn format list Self comptime fmt const u8 options stdfmtFormatOptions out_stream anytype void try out_streamwriteAll var it listfirst while it node it nodenext try stdfmtformatType nodedata fmt options out_stream 1 try out_streamwriteAll try out_streamwriteAll pub fn main void const ListU32 LinkedList u32 var list ListU32 var node1 ListU32Node data 1 var node2 ListU32Node data 2 var node3 ListU32Node data 3 listprepend node1 listprepend node2 listprepend node3 try stdoutprint n list try stdoutprint bn list 輸出 3 2 1 11 10 1 帶分配器的字串重複 const std import std fn repeat allocator stdmemAllocator original const u8 times usize stdmemAllocatorError const u8 var buffer try allocatoralloc u8 originallen times for 0times i stdmemcopyForwards u8 buffer originallen i original return buffer pub fn main void const stdout stdiogetStdOut writer var arena stdheapArenaAllocatorinit stdheappage_allocator defer arenadeinit var allocator arenaallocator const original Hello const repeated try repeat allocator original 3 輸出 Hello Hello Hello try stdoutprint sn repeated 輸出 Hello Hello Hello 社群 Zig 軟體基金會（ZSF）有一個非常活躍的貢獻者社群，並且仍處於早期發展階段。 儘管如此，2024 年的一項 Stack Overflow 調查發現，Zig 軟體開發人員的平均年薪為 103000 美元，使其成為薪資最高的程式語言之一。 然而，只有 083 的受訪者表示他們精通 Zig。 專案 Bun 是一個用 Zig 編寫的 JavaScript 和 TypeScript 執行時，使用 Safari 的 JavaScriptCore 虛擬機器。 Ghostty 是一個用 Zig 編寫的 終端模擬器。 TigerBeetle 是一個用 Zig 編寫的金融交易資料庫。 參見 C C D Nim Go Rust Carbon V 參考文獻 引用 書目 外部連結 影片：介紹 Zig 影片：The Road to 10 Zig Weekly 中文社群 Category 高階程式語言 Category 跨平臺軟體 Category 自由跨平臺軟體 Category 嵌入式系統 Category 自由和開源編譯器 Category 程式語言 Category 靜態型別程式語言 Category 系統程式語言 CategoryMIT 軟體 CategoryC 程式語言 編譯器