Serializing structs as zig source code

suppose i have the following definition in an upstream zig program:

const Pair = struct {
    x: u32,
    y: u32,

var p = Pair{ .x = 10, .y = 20 };

then imagine that i’ve written the expression Pair{ ... } to some generated zig source – which i can then later @import into a downstream zig program that ALSO has the same definition of the Pair type…

my use-case is that i need to do this generically – which is to say i’m starting with some variable s of some struct type S… i can clearly reflect on the fields of S and generate the appropriate { ... } values…

but how do i get the “struct type name”??? @typeName(@TypeOf(s)) gives me a unique qualified name that encodes the module name as a prefix… do i parse this in some way???

my specific use-case ALSO involves generating some zig code adjacent to these serialized structs, hence i’m looking to avoid generating (say) a separate json file for data…

FWIW, the same sort of problem occurs with enum types…

The @typeName gives you the fully qualified name of the type. This string is period separated and the last part is the name of the struct, union or enum type.

const std = @import("std");

const Pair = struct {
    x: u32,
    y: u32,

/// Returns the last part of a '.' separated string.
/// Returns the entire string if there is no '.' separator.
fn nameOf(fqn: []const u8) []const u8 {
    if (std.mem.lastIndexOf(u8, fqn, ".")) |index| {
        return fqn[index + 1 ..];
    return fqn;

pub fn main() void {
    const p = Pair{ .x = 10, .y = 20 };
    const fqn = @typeName(@TypeOf(p));
    std.debug.print("{s} {s}\n", .{ fqn, nameOf(fqn) });

that part i can see… what’s not obvious is how i can transform everything EXCEPT the struct name into an @import that defines that type…

depending on whether the struct is defined (say) in std.builtin or perhaps in my own module imported through some root-relative path is my current challenge…

i believe i have solution for a “known” set of modules imported relatively to my root… i was just wondering if there was a more solution to the problem of mapping these typenames into some @import("<path>").T text…

I am assuming that you are aware that you can only import using fixed string literals.

error: @import operand must be a string literal
const std = @import(constant);

Your only option is to generate a file where the import path is also generated.

I cannot think any way to connect the imported files and the types with their namespaces, other than parsing all the files and generating the type namespaces while keeping track of the imports.

1 Like

that’s basically what i’ve done, with types effectively restricted to a known set of modules…

i can easily “recognize” the types defined in my own modules (since i have a list created even further upstream)…

types that are defined in some module outside my root (eg., builtin.ExportOptions) can likely become @import("builtin").ExportOptions

i think i’m good with this…