Zig 爲什麼可以判斷兩個類型相等?

最近在和一些朋友線下交流,分享 Zig 時,都不約而同的提了一個問題,Zig 是如何判斷下面這兩個類型是一樣的:

fn LinkedList(comptime T: type) type {
    return struct {
        pub const Node = struct {
            prev: ?*Node,
            next: ?*Node,
            data: T,
        };

        first: ?*Node,
        last:  ?*Node,
        len:   usize,
    };
}

test "linked list" {
    try expect(LinkedList(i32) == LinkedList(i32));
}

我們知道,類型在 Zig 中是一等成員,和 i8、f32 類似,那上面調用了兩次 LinkedList(i32),爲什麼他們會是相等的呢?僅僅因爲他們包含的字段一樣?

在動態語言裏,這其實叫做 Duck Typing[1],即鴨子類型,一個在 Python 中的例子:

class Duck:
    def swim(self):
        print("Duck swimming")

    def fly(self):
        print("Duck flying")

class Whale:
    def swim(self):
        print("Whale swimming")

for animal in [Duck(), Whale()]:
    animal.swim()
    animal.fly()

輸出

Duck swimming
Duck flying
Whale swimming
AttributeError: 'Whale' object has no attribute 'fly'

如果因爲鴨子會游泳,所以可以假設任何會游泳的東西都是鴨子,那麼鯨魚就可以被認爲是鴨子; 然而,如果還假設鴨子必須會飛,那麼鯨魚就不會被認爲是鴨子。

爲了簡化 Zig 中的問題,定義以下兩個類型:

const StaticA = struct { foo: i8 };

pub fn main() !void {
    const dynamic_struct = std.builtin.Type.Struct{
        .layout = .Auto,
        .fields = &[1]std.builtin.Type.StructField{.{
            .name = "foo",
            .type = i8,
            .default_value = null,
            .is_comptime = false,
            .alignment = 1,
        }},
        .decls = &.{},
        .is_tuple = false,
    };
    const DynamicA = @Type(.{ .Struct = dynamic_struct });
    try std.testing.expect(DynamicA == StaticA);
}

上述代碼通過兩種方式生成了具有相同字段的兩個類型,都只有一個字段,且名字類型一致,但最後 expect 會報錯,說明這是兩個不同的類型,即便他們具有相同的字段。

那麼一開始的例子裏,爲什麼兩個 LinkedList(i32) 會被認爲相等呢?這其實是由於 Zig 的一種特殊實現,即在 comptime 期間執行的函數,結果會緩存下來,因此兩次調用返回的是同一值,所以他們相等。

引用鏈接

[1] Duck Typing: https://en.wikipedia.org/wiki/Duck_typing

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