dynamic construct a type in comptime

Zig 可以通过 comptime 来实现 generic,但官网给的例子还是比较简单的:

fn List(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,
    };
}

// The generic List data structure can be instantiated by passing in a type:
var buffer: [10]i32 = undefined;
var list = List(i32){
    .items = &buffer,
    .len = 0,
};

这个例子中,构造的 List(i32) 还是感觉不够动态,譬如,是否可以:

  • 结构体的成员数量、类型是动态的?
  • 结构体内的 fn 是动态的?

这一切的奥秘,隐藏在 @typeInfo, @Type 这几个内置函数中。如下是一个简单的示例: User2 是一个 comptime 动态计算出来的类型,其一部份 字段是从 User 这个模版类型中复制来的,email 字段则是动态添加上去的。


const std = @import("std");

// used as a type Template
const User = struct {
    name: [:0]const u8,
    age: u32,
};

pub fn main() void {

    const print = std.debug.print;
    const t_info: std.builtin.Type = @typeInfo(User);

    // dynamic construct a Type
    const t_info2: std.builtin.Type = .{
       .Struct = .{
          .layout = t_info.Struct.layout,
           .backing_integer =  t_info.Struct.backing_integer,
           .fields = & .{
               .{
                   .name = "NAME",
                   .type = t_info.Struct.fields[0].type,
                   .default_value = t_info.Struct.fields[0].default_value,
                   .is_comptime = t_info.Struct.fields[0].is_comptime,
                   .alignment = t_info.Struct.fields[0].alignment
               },
               .{
                   .name = "AGE",
                   .type = t_info.Struct.fields[1].type,
                   .default_value = t_info.Struct.fields[1].default_value,
                   .is_comptime = t_info.Struct.fields[1].is_comptime,
                   .alignment = t_info.Struct.fields[1].alignment
               },
               .{
                   .name = "email",
                   .type = [:0]const u8,
                   .default_value = null,
                   .is_comptime = false,
                   .alignment = 1
               }
           },
           .decls = t_info.Struct.decls,
           .is_tuple = false
       }
    };

    // build type User2
    const User2 = @Type(t_info2);

    // now, User2 can be used in source code.
    const u: User2 = .{
        .NAME = "WANGZX",
        .AGE = 20,
        .email = "wangzx@qq.com",
    };

    print("Users = {any}\n", .{User2});
    print("u.NAME = {s}, u.AGE = {d} u.email = {s}\n",
        .{ u.NAME, u.AGE, u.email });
}

参考

  • Zig Cli: 处理 CLI 是 comptime 的一个很实用的场景。 rust/scala 都玩这个。

结论

  1. std.builtin.Type 类似于 scala.quotes.TypeRepr,是 comptime 时用于描述 Type 的元数据结构。
  2. 目前来看,并没有提供动态构建一个 Fn ,即操作 AST 的 API。因此,动态构建的类型,还是有一些局限的。