Some opinion about zig

2025-01-09 33:06:12/1小时之前

Some off-topic i think i won’t use chinese write blog anymore. (Maybe I’ll clarify later.)

I’m a zig noob and have been using it about 2 weeks. I feel that the language fits some of my ideas when i use it, but it still has some problems and it’s not good enough.

The few ways to learn

Normally, when i start learning a new skill, I search for as much information as possible about it. But there is little information about zig that i can get. The official website only introduces basic syntax and provides a list of meta field. To be honest, I have other language skill so that the basic syntax cost 5 minis for me :)

My first zig project is a simple ini format. I create this project and write a simple lex program at first time. Write a lex program for ini is easy, eat each char and put the result to the defined token filed.

pub const Token = struct {
  kind: Kind
  pub const Kind = enum(u8) {
      end_of_file,
      open_bracket,
      close_bracket,
      comment,
      equal,
      string,
      whitespace,
      break_line,
  }
  /// ...
}

It’s been fun till here, until i wrote parser. In golang i can create a set of duck type based node just like

type R interface {
  Equal() bool
}

type Rule struct {
  Data R
}


type AtRule struct {
  Token uin8
}

func (*AtRule) Equal() bool {
  return true
}

// ... etc

emmm, How can i declare the same describe in zig? BTW zig don’t have any official interface keyword and don’t have trait. So how can i do it? So i searched for related content on Ziggit and i find the answer. (About interface? No i’ll explain later)

We can create a basic node and ohter node like

pub const Rule = struct {
  kind: Kind,

  pub const AtRule = struct {
      base: Rule = .{ .kind = .at_rule }
  }

  // ...etc
};

The using std.ArrayListUnmanaged to save us node. we only save the node ptr! I think the problem about save node it’s i’m a gopher …hhh Zig don’t have official interface so my experience is invalid. And we using zig meta program at the stringify time.

Declare a interface

The above section is some of problem i encountered when writing zig programs. This section is a idea about zig interface. Zig’s level of abstraction is just right, but not enough. There’re two ways to descibe interface on zig.

  • Tagged Unions

const Writer = union(enum) {
  file: File,

  fn writeAll(self: Writer, data: []const u8) !void {
    switch (self) {
      .file => |file| return file.writeAll(data),
    }
  }
};

const File = struct {
  fd: os.fd_t,

  fn writeAll(self: File, data: []const u8) !void {
    _ = try std.os.write(self.fd, data);
  }
};


pub fn main() !void {
  const file = File{.fd = std.io.getStdOut().handle};
  const writer = Writer{.file = file};
  try writer.writeAll("hi");
}
  • vtable

const Writer = struct {
  ptr: *anyopaque,
  writeAllFn: *const fn (ptr: *anyopaque, data: []const u8) anyerror!void,

  fn writeAll(self: Writer, data: []const u8) !void {
    return self.writeAllFn(self.ptr, data);
  }
};

const File = struct {
  fd: os.fd_t,

  fn writeAll(ptr: *anyopaque, data: []const u8) !void {
    // This re-establishs the type: *anyopaque -> *File
    const self: *File = @ptrCast(@alignCast(ptr));
    // os.write might not write all of `data`, we should really look at the
    // returned value, the number of bytes written, and handle cases where data
    // wasn't all written.
    _ = try std.os.write(self.fd, data);
  }

  fn writer(self: *File) Writer {
    return .{
      // this "erases" the type: *File -> *anyopaque
      .ptr = self,
      .writeAllFn = writeAll,
    };
  }
};

References

CC BY-NC-SA 4.02024-PRESENT © Kanno