zigでのとりあえずC言語との連携的な方法が断片的に分かったので、その方法を記載する。

実行ファイルでCを読ませたい場合、exe定数にて*CompileaddIncludePath()addCSourceFiles()linkLibC()メソッドを持ってるから、それを呼べば良いらしい。

pub fn build(b: *std.Build) void {
//...
    const exe = b.addExecutable(.{
        .name = "zig-test",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    exe.addIncludePath(.{ .path = "src" });
    exe.addCSourceFiles(.{ .files = &.{
        "src/util.c",
    } });
    exe.linkLibC();
//...
}

以下のようなCヘッダがあったとして、

#pragma once

typedef struct Value
{
    int value;
    int (*get)(struct Value *);
    void (*add)(struct Value *, int);
    void (*sub)(struct Value *, int);
    void (*print)(struct Value *);
} Value;

Value createValue(int v);

int getValue(struct Value *self);

void addValue(struct Value *self, int v);

void subValue(struct Value *self, int v);

void printValue(struct Value *self);

定義ファイルはこんな感じにしておいた場合、

#include <stdio.h>
#include "util.h"

Value createValue(int v)
{
    Value value;
    value.value = v;
    value.get = getValue;
    value.add = addValue;
    value.sub = subValue;
    value.print = printValue;
    return value;
}

int getValue(Value *self)
{
    return self->value;
}

void addValue(Value *self, int v)
{
    self->value += v;
}

void subValue(Value *self, int v)
{
    self->value -= v;
}

void printValue(Value *self)
{
    printf("Value: %d\n", self->value);
}

以下みたいな感じにしたら動いた

const std = @import("std");
const util = @cImport(@cInclude("util.h"));

pub fn main() !void {
    var value = util.createValue(100);
    if (value.get) |get| {
        std.debug.print("Value From C: {}\n", .{get(&value)});
    }
    if (value.add) |add| {
        add(&value, 100);
    }
    if (value.sub) |sub| {
        sub(&value, 50);
    }
    if (value.print) |print| {
        print(&value);
    }
}

実行結果は↓ とりあえずこういった設定だとzig build runじゃなきゃだめみたい。 あまり細かいところは分かっていない…

vscode  /workspaces/zig-test $ zig build run
Value From C: 100
Value: 150

とはいえ、zig側で固く型検証してくれるのは良いかも。慣れればC側の技術を上手く使ったり、逆を使ったりして出来るんだろうね。
とりあえず、Cのプログラムがそのまま確かに動いちゃう。zigってこのまま混ぜ混ぜして組み込みデバイスやWebAssemblyにも持っていける(かもしれない?)らしいから、そう思うと可能性は広がるね。Uberがやっていると言うPC向け汎用コンパイラとしての使い方は中々アリかも。

ファイルサイズが超コンパクト

おそらくzigがすごいのはこれじゃないでしょうか。恐らくRustで作成するより更に小さいです。普通のCとどっこいどっこいじゃないかと。それでパフォーマンスが良い、のがこの言語の1つの魅力かと。
以下は上のプログラムのバイナリの結果です。
正直Goの極厚サイズでも気にしないケースなんて多いわけで、Rustレベルで十分でしょ、と思うことが大半だと思いますが、wasm (wasi)もちゃんと動きました。

.rwxr-xr-x  7.6k xxxxx  2  9月 xxxxx zig-test
.rwxr--r--   29k xxxxx  2  9月 xxxxx zig-test.wasm

ちなみに以下のコマンドを使った。(オプションをよく分かってないのでメモで)

zig build -Dtarget wasm32-wasi -Doptimize=ReleaseSmall