热门问题
时间线
聊天
视角
Zig
来自维基百科,自由的百科全书
Remove ads
Zig(也稱為 Ziglang)[12] 是一種命令式、通用、靜態型別、編譯語言的系統程式語言,由 Andrew Kelley 設計。[13]它旨在作為 C 的繼任者,提供更輕量更簡單的編程體驗,同時提供更豐富的功能。[14] 它是自由及開放原始碼軟件,在MIT許可證下發布。
此條目翻譯品質不佳。 (2025年1月5日) |
Zig 語言的簡化涉及到流控制、函數呼叫、庫匯入、變量聲明和 Unicode 支援。然而,Zig 不使用 宏 或 預處理 指令。Zig 從現代語言引入的特性包括 編譯時泛型編程資料類型,允許函數編譯時處理各種數據,並添加了一小套新的 編譯器 指令,以允許通過 反射編程 訪問和修改這些類型的資訊。
Zig 的另一組新增功能旨在提高代碼安全性。像 C 一樣,Zig 沒有實現 垃圾回收,其 主記憶體管理 是手動的。對於包括此種情況的錯誤處理,Zig 引入了 可選類型,他們的 語法 很簡單,而 Zig 內建於語言中的 單元測試 框架也使提前預知錯誤變得簡單和方便。
Remove ads
描述
Zig 的主要目標是成為解決目前由 C 解決的各種任務的更好的解決方案。對此的主要關注點是可讀性;Zig 儘可能使用現有概念和語法,避免為相似概念添加不同的語法。此外,它的設計目標是「穩健性、最佳化和可維護性」,包括各種提高安全性、最佳化和測試的功能。簡潔的語法是維護的重要組成部分,因為該語言的目標是允許維護人員在不需要學習他們可能不熟悉的語言細節的情況下除錯代碼。[15] 即使有這些更改,Zig 也可以編譯並與現有的 C 代碼一起使用;可以在 Zig 專案中包含 C 標頭檔並呼叫它們的函數,並且,通過包含編譯器生成的標頭檔,Zig 代碼可以被連結到 C 專案。[16]
為了保持整體設計理念的簡潔和易讀,Zig 系統整體相比於 C 及其他類似 C 的語言也包含了一些風格上的變化。例如,Rust 語言具有 運算子多載,這意味着類似 a = b + c
的陳述式實際上可能是對類型的多載版本的加運算子的函數呼叫。此外,該函數可能會引發 panic,從而可能中斷任何後續代碼。在 Zig 中,如果某個東西呼叫了一個函數,那麼它看起來就像一個函數呼叫;如果沒有呼叫,它看起來就不會像一個函數呼叫。如果它可能投擲錯誤,則一定會在代碼中顯式表示它可能投擲錯誤,[16] 而錯誤處理可以通過錯誤類型處理,也可以通過 catch 或 try 處理。
Zig 的目標與同時期設計的許多其他語言(如 Go、Rust、Carbon 和 Nim)的目標形成對比。通常,這些語言更複雜,他們添加了運算子多載、看起來像值(屬性)呼叫的函數等許多功能,這些旨在幫助構建大型程式。並且這些特性更接近於 C++ 的特性,而這些語言也更像 C++。[16] Zig 在型別系統的擴充上更為保守,支援編譯時泛型,並通過 comptime
特性實現了一種 鴨子型別 形式的用法。
Remove ads
C 程式中錯誤的主要來源之一是基於malloc的主記憶體管理系統。malloc 會為代碼使用分配一塊主記憶體,並返回該主記憶體的參照作為一個 指標,但C沒有一套系統來確保在程式不再需要主記憶體時釋放該主記憶體,這可能導致記憶體流失以至於耗費完所有的可用主記憶體。更常見的是懸空指標,這些指標參照未正確分配的主記憶體地址。[17]
解決這些問題的常見方法是垃圾回收(GC),它會檢查程式中指向先前分配過主記憶體的指標,並刪除所有的不再有指標指向它們的主記憶體塊。儘管這在很大程度上減少甚至消除了主記憶體錯誤,但GC系統的速度相對較慢,[來源請求],並且具有不可預測的效能,使其不適用於系統編程。另一種解決方案是自動參照計數(ARC),其通過維護指向一個主記憶體塊的指標數,並在指標建立和銷毀時更新計數來實現基本相同的功能,這意味着其不需要執行詳盡的指標搜尋,而僅增加每個指標在執行建立和銷毀操作時更新參照計數器的開銷。[17]
Zig 旨在提供與 C 相似或更好的效能,因此 GC 和 ARC 不是合適的解決方案。相反,它使用一種現代的(截至 2022 年),被稱為可選類型的概念。與指標可以指向空值或 nil 不同,這種方案使用一個單獨的類型來指示可能為空的數據。這類似於使用一個指標和一個布林值的結構來指示指標是否有效,但布林值的狀態由語言隱式管理,不需要程式設計師顯式管理。因此,例如,當聲明指標時,它被設置為「未分配」,而當該指標從 malloc 接收一個值時,如果 malloc 成功,它將被設置為「已分配」。[18]
這種模型的優點是它具有非常低甚至是零的開銷;儘管這使得編譯器必須建立在操作指標時傳遞可選類型,而不是一個簡單的指標的代碼,但這允許它在編譯時直接檢查出可能的主記憶體問題,而無需延後到執行時檢查。例如,在 C 中建立一個具有空值的指標並嘗試使用它是完全可以接受的,儘管這會導致空指標錯誤。相比之下,使用可選類型的語言可以保證所有代碼只會在指標有效時嘗試使用相應的指標。雖然這不能消除所有潛在問題,但當在執行時發生問題時,錯誤可以被更精確地定位和解釋。[19]
Zig 中主記憶體管理的另一變化是:實際分配操作是通過 struct
描述相應的操作來處理的,而不是直接呼叫 libc 中的主記憶體管理函數。例如,在 C 中,如果想寫一個包含多個副本的字串的函數,該函數可能如下所示:
const char* repeat(const char* original, size_t times);
在代碼中,該函數會檢查 original
的大小,然後 malloc times
的主記憶體來為它將構建的字串預留空間。但這個 malloc 對呼叫它的函數是不可見的,所以,如果像 repeat
這樣的函數沒有正常釋放主記憶體,就會發生記憶體流失。而在 Zig 中,這種操作可能通過如下函數處理:
fn repeat(allocator: *std.mem.Allocator, original: []const u8, times: usize) std.mem.Allocator.Error![]const u8;
在此代碼中,allocator
變量傳遞了一個主記憶體分配器,並且 repeat
函數則返回結果字串或 Allocator.Error。通過直接將分配器作為輸入傳參,使得主記憶體分配不會被「隱藏」起來,即呼叫鏈上的所有主記憶體分配都會通過這個分配介面進行分配。順帶一提,Zig 的標準庫內沒有進行任何隱式主記憶體分配。此外,由於結構可以指向任何東西,可以使用替代分配器,甚至是程式中編寫的分配器。這使不使用通常分配整個主記憶體頁的作業系統函數的小對象分配器等操作成為了可能。[20]
可選類型是一個提供通用功能但仍然簡單和通用的語言特性。它不僅可以用於解決空指標問題,還可以明確地描述「無值」場景。考慮一個名為 countTheNumberOfUsers
的函數,它返回一個整數,以及一個整數變量,theCountedUsers
,它儲存結果。在許多語言中,會在 theCountedUsers
中放置一個 魔術數字 以表示 countTheNumberOfUsers
尚未被呼叫,而許多實現則會將其設置為零。在 Zig 中,這可以實現為 var theCountedUsers: ?i32 = null
,將變量設置為明確的「未被呼叫」的值。[20]
Zig 另一個有助於管理主記憶體問題的更通用特性是 defer
的概念,由它標記的代碼在函數結束時無論如何都會被執行,包括可能的執行時錯誤。如果某個特定函數分配了一些主記憶體,然後在操作完成時釋放它,可以添加一行代碼,推遲一個 free
,以確保無論發生什麼都會釋放它。[20] 但需要注意的是,defer
並不完全等價於 RAII,在某些場景下,會有許多區別之處。[21]
Zig 主記憶體管理避免了隱式分配。即分配不直接由語言本身進行管理。相反,用戶需要通過 標準庫 明確地進行堆訪問來進行顯式主記憶體管理。[22]
Remove ads
Zig 提倡一種將新 Zig 代碼與現有 C 代碼結合的漸進方法。為此,它可以儘可能無縫地與現有 C 庫進行互動。Zig 使用 @import
指令匯入庫,通常如下所示:
const std = @import("std");
如此便可以呼叫 std 內的函數,例如:
std.debug.print("Hello, world!\n", .{});
要載入 C 代碼,只需將 @import
替換為 @cImport
:
const c = @cImport(@cInclude("soundio/soundio.h"));
然後我們就可以像呼叫本地 Zig 代碼一樣呼叫 soundio 庫中的函數。由於 Zig 使用新資料類型,它們是顯式定義的,不像 C 的更通用的 int
和 float
,我們需要使用少量指令在 C 和 Zig 類型之間流動數據,包括 @intCast
和 @ptrCast
。[20]
Remove ads
Zig 將交叉編譯視為語言的一級用例。這意味着任何 Zig 編譯器都可以為其目標平台之一生成可執行的二進制檔案,這些平台包括數十種。這些不僅包括廣泛使用的現代系統,如 ARM 和 x86-64,還包括 PowerPC、SPARC、MIPS、RISC-V 甚至 IBM 的 z/Architectures (S390)。工具鏈可以編譯到這些目標中的任何一個,而無需安裝額外的軟件,因為所有需要的支援都在基本系統中。[20]
通過使用 comptime
關鍵字,程式設計師可以顯式地在 編譯時 而不是 執行時 評估代碼段。能夠在編譯時執行代碼使得 Zig 具有 宏 和 條件編譯 的功能,而無需單獨的 預處理器 語言。[23]
在編譯時,類型成為 一級公民。這使得在編譯時 鴨子型別 成為可能,這也是 Zig 實現泛型類型的方式。[24]
例如,在 Zig 中,一個泛型的 鏈結串列 類型可以使用如下函數實現:
fn LinkedList(comptime T: type) type;
這個函數接收某種類型 T
,並返回一個自訂的 struct
,定義了包含該資料類型的鏈結串列。
據報道,「Zig」這個名字是通過一個涉及 Python 指令碼的過程選出的,該指令碼隨機組合字母,從字母「Z」開始,然後跟隨一個元音或「Y」,以生成四個字母的單詞。儘管目標長度是四個字母,但在生成的各種組合中,最終選擇了三個字母的單詞「Zig」。[25]
其他特性
Zig 支援 編譯時 泛型編程、反射編程 和評估、交叉編譯 以及 手動主記憶體管理。[26] 該語言的一個主要目標是改進 C 語言,[23][27] 同時也從 Rust 等語言中汲取靈感,[28][16] 等等。Zig 具有許多用於 低階程式語言 的特性,特別是緊湊結構(沒有欄位之間的填充的結構)、任意寬度的整數[29] 和多種指標類型。[24]
Zig 不僅僅是一種新語言:它還包括一個 C/C++ 編譯器,可以與這兩種語言一起使用。
缺點
Zig 有一些缺點。如果主記憶體沒有正確釋放,由於缺乏隱式控制,可能會因為忘記 defer
等必要的手動操作導致記憶體流失。[30] 對於那些不熟悉低階編程概念的人來說,學習 Zig 的曲線可能很陡峭。[30] 儘管 Zig 社區在不斷壯大,但截至 2024 年,它仍然是一個新的語言,在成熟度、生態系統和工具方面有待改進。[30] 與其他語言的互操作性可能會帶來挑戰,因為這通常需要額外的努力來管理數據編組和通訊。[30] 最後,複雜用例的學習資源有限,儘管隨着興趣和採用的增加,這種情況正在逐漸改善。[30]
版本
自 0.10 版本以來,(新的預設)Zig 編譯器是用 Zig 程式語言編寫的,即它是一個 自舉編譯器,這是該版本的一個重大新特性。舊的遺留 自舉 編譯器是用 C++ 編寫的,仍然是一個選項,但在 0.11 版本中將不再是選項。當使用新的 Zig 編譯器進行編譯時,使用的主記憶體要少得多,並且編譯速度稍快。舊的、現在的遺留 C++ 編譯器使用了 3.5 倍的主記憶體。
Zig 的預設最佳化後端仍然是 LLVM,[31] 並且 LLVM 是用 C++ 編寫的。帶有 LLVM 的 Zig 編譯器是 169 MiB[需要解釋],而不帶 LLVM 的則是 4.4 MiB。通常情況下,使用新的基於 Zig 語言的編譯器編譯的可執行代碼更快,其 LLVM 代碼生成更好,並修復了許多錯誤,但在 0.10 版本中也對舊的遺留編譯器進行了改進。自寄存連結器與自寄存編譯器緊密耦合。新版本還增加了一些對 AMD GPU 的實驗性(第 3 層)支援(原始碼中還有對 Nvidia GPU 和 PlayStation 4 和 5 的一些較少支援)。
舊的 自舉 ("stage1") 編譯器是用 Zig 和 C++ 編寫的,使用 LLVM 13 作為後端,[32][33] 支援其許多本機目標。[34] 編譯器也是 自由及開放原始碼軟件,在 MIT 許可證 下發布。[35] Zig 編譯器提供了類似於 Clang 的編譯 C 和 C++ 的能力,命令為 zig cc
和 zig c++
,[36] 提供了包括 C 標準庫 (libc) 和 C++ 標準庫 (libcxx) 在內的許多不同平台的標頭檔,允許 Zig 的 cc
和 c++
子命令作為開箱即用的 交叉編譯器。[37][38]
此外,官方支援(並記錄)的作業系統(主要是桌面作業系統)上可以製作(最小)應用程式,這些應用程式也可以為 Android(使用 Android NDK)和 iOS 編程。
Remove ads
包管理
在0.11.0版本之前,Zig沒有內建的包管理器,但在0.11.0版本中包含了一個實驗性的包管理器,並在0.12.0中進一步擴充。
Zig沒有官方的包倉庫;相反,包只是一個指向歸檔檔案或Git倉庫的URL。理想情況下,當解壓縮時,包括一個標準的build.zig
檔案(Zig 編譯器按慣例使用該檔案來編譯原始碼)和一個build.zig.zon
檔案,用於定義包的名稱和版本。
Zig 的開發由 Zig 軟件基金會(ZSF)資助,ZSF 是一個由 Andrew Kelley 擔任總裁的非營利公司,接受捐贈並僱傭多名全職員工。[39][40][41]
範例
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Hello, {s}!\n", .{"world"});
}
const std = @import("std");
const stdout = std.io.getStdOut().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_node.next = list.first;
list.first = new_node;
}
pub fn format(
list: Self,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
try out_stream.writeAll("( ");
var it = list.first;
while (it) |node| : (it = node.next) {
try std.fmt.formatType(
node.data,
fmt,
options,
out_stream,
1,
);
try out_stream.writeAll(" ");
}
try out_stream.writeAll(")");
}
};
}
pub fn main() !void {
const ListU32 = LinkedList(u32);
var list = ListU32{};
var node1 = ListU32.Node{ .data = 1 };
var node2 = ListU32.Node{ .data = 2 };
var node3 = ListU32.Node{ .data = 3 };
list.prepend(&node1);
list.prepend(&node2);
list.prepend(&node3);
try stdout.print("{}\n", .{list});
try stdout.print("{b}\n", .{list});
}
- 輸出
( 3 2 1 ) ( 11 10 1 )
const std = @import("std");
fn repeat(
allocator: *std.mem.Allocator,
original: []const u8,
times: usize,
) std.mem.Allocator.Error![]const u8 {
var buffer = try allocator.alloc(
u8,
original.len * times,
);
for (0..times) |i| {
std.mem.copyForwards(
u8,
buffer[(original.len * i)..],
original,
);
}
return buffer;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var arena = std.heap.ArenaAllocator.init(
std.heap.page_allocator,
);
defer arena.deinit();
var allocator = arena.allocator();
const original = "Hello ";
const repeated = try repeat(
&allocator,
original,
3,
);
// 输出 "Hello Hello Hello "
try stdout.print("{s}\n", .{repeated});
}
- 輸出
Hello Hello Hello
社區
Zig 軟件基金會(ZSF)有一個非常活躍的貢獻者社區,並且仍處於早期發展階段。[42] 儘管如此,2024 年的一項 Stack Overflow 調查發現,Zig 軟件開發人員的平均年薪為 103,000 美元,使其成為薪資最高的程式語言之一。[43] 然而,只有 0.83% 的受訪者表示他們精通 Zig。[42]
專案
- Bun 是一個用 Zig 編寫的 JavaScript 和 TypeScript 執行時,使用 Safari 的 JavaScriptCore 虛擬機器。
- Ghostty[44] 是一個用 Zig 編寫的 終端模擬器。
- TigerBeetle[45] 是一個用 Zig 編寫的金融交易資料庫。
參見
參考文獻
外部連結
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads