Zig defer 慣用法

原文地址:Zig defer Patterns[1]

裏面講述了 defer 除了做資源回收外,其他的一些慣用法,裏面有幾個有趣的點。

第一點是這個:

errdefer comptime unreachable

文中稱這個是 Zig 的巔峯用法😅, errdefer unreachable 還比較好理解,即在執行出錯時,執行 unreachable ,那加上 comptime 呢?

其實這是阻止 Zig 編譯器生產錯誤處理的代碼,即在編譯時期保證下面的邏輯不會出錯,確實用的很巧妙!一個簡單的例子來說明:

const std = @import("std");

test "errdeferWithUnreachable" {
    errdefer comptime unreachable;
    const i = try inc(1);
    std.testing.expectEqual(i, 2);
}

fn inc(a: i8) !i8 {
    if (a > 10) {
        return error.TooLarge;
    }
    return a + 1;
}

直接執行 zig test ,在編譯時會報下面的錯誤:

test.zig:4:23: error: reached unreachable code
    errdefer comptime unreachable;

雖然 a 是個運行時的值,但是 errdefer comptime unreachable 不關心這個,只要 Zig 編譯器開始生成 ErrorSet 相關代碼路徑,編譯就會報錯,去掉上面的 if 判斷測試就可以正常運行。 一個很好的實際例子:std.hash_map: fix pointer lock safety false positive by andrewrk · Pull Request #19364 · ziglang/zig[2]

   assert(std.math.isPowerOfTwo(new_cap));
 
   var map: Self = .{};
-  defer map.deinit(allocator);
-  map.pointer_stability.lock();
   try map.allocate(allocator, new_cap);
+  errdefer comptime unreachable;
+  map.pointer_stability.lock();
   map.initMetadatas();
   map.available = @truncate((new_cap * max_load_percentage) / 100);
 
@@6581,7 @@ pub fn HashMapUnmanaged(
   self.size = 0;
   self.pointer_stability = .{ .state = .unlocked };
   std.mem.swap(Self, self, &map);
+  map.deinit(allocator);
 
+
+test "getOrPut allocation failure" {
+    var map: std.StringHashMapUnmanaged(void) = .{};
+    try testing.expectError(error.OutOfMemory, map.getOrPut(std.testing.failing_allocator, "hello"));
+}

可以看到, 這麼修改後,就可以保證 map.deinit(allocator); 語句之前沒有錯誤可能產生!讀者可以細細品味一下這個用法。

另一個小技巧是 errdefer 竟然支持錯誤捕獲,即下面這種用法:

const port = port: {
    errdefer |err| std.log.err("failed to read the port number: {!}", .{err});
    var buf: [fmt.count("{}\n", .{maxInt(u16)})]u8 = undefined;
    const len = try process.stdout.?.readAll(&buf);
    break :port try fmt.parseInt(u16, buf[0 .. len -| 1], 10);
};

ZigCC

引用鏈接

[1] Zig defer Patterns: https://matklad.github.io/2024/03/21/defer-patterns.html
[2] std.hash_map: fix pointer lock safety false positive by andrewrk · Pull Request #19364 · ziglang/zig: https://github.com/ziglang/zig/pull/19364/files

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dLniqksAjHrErJ6mLhDiNw