How to pretty print structures containing string?

For example, I want to pretty print uname for debug purpose.

    const uname = std.os.uname();
    std.debug.print("{any}\n", .{uname});

However, the output is

os.linux.utsname{ .sysname = { 76, 105, 110, 117, 120, 0, ...

It doesn’t display the string, but only the number sequence.

Use “s” instead of “any” to print a string.

    const uname = std.os.uname();
    std.debug.print("{s}\n", .{uname});

documentation: Documentation - Zig

I would like to know if there’s a simple(r) way to do this, as I had to write a function just to print a struct field by field, for debugging purposes.

1 Like

well, it produces this error:

/usr/lib/zig/lib/std/fmt.zig:470:5: error: invalid format string 's' for type 'os.linux.utsname'

I think s can only be used with string, not with structure.

2 Likes

Indeed: https://zigbin.io/1b2df1

1 Like

Oh, sorry, I missed that uname is a structure.

You can print each string member of the structure:

    const uname = std.os.uname();
    std.debug.print("sysname={s} machine={s}\n", .{uname.sysname, uname.machine});
1 Like

Yes, it does the job, but is unhandy.

I think the original question was about a way to automatically format []const u8 fields as strings rather than slices. For user-defined types (struct, union) one can custom-define format callback that will be automatically applied by main format function but I am not aware of the way to custom define format for existing types like []const u8.

1 Like

It can be done. The inner types are C zero terminated strings.
This trick (pretend that uname is a json object) does not work properly, but it is a better option:

    const stdout = std.io.getStdOut().writer();
    var ws = std.json.writeStream(stdout, .{ .whitespace = .indent_2 });
    defer ws.deinit();
    ws.write(uname) catch unreachable;
1 Like

I see there is a similar post here: How to print struct with strings - #10 by marler8997

We can use this

std.log.info("response is {}", .{std.json.fmt(response)});
5 Likes

Looks like you have to add a second parameter when calling std.json.fmt, a StringifyOptions struct. Using default field values could be OK, is this case just do

std.json.fmt(response, .{})

Or go with one of the indentation options instead:

std.json.fmt(response, .{ .whitespace = .indent_4 })

Also, today I needed to pretty print some structs for debugging purposes, and I have not noticed this std.json.fmt trick, so… I wrote a quick and dirty pretty print for myself. Here it is.

Had I known about std.json.fmt, I would not have spent my time writing it. On the other hand, my code (which does not cover many types, e.g. optionals, enums, unions) still produces a different printout compared to std.json.fmt - for example it prints field types.

Also you can change this code however you want, if you need different output. Sure, you can do the same with std.json.fmt, but I guess if you also use it for its intended purpose, this could become a hassle.

Sample output of the linked code:

{
    comptime x: u8 = 123
    tea: u32 = 123
    lemon: f32 = 1.2300000190734863
    cake: []const u8 = "Yes, please!"
    ham: test_ret_anon_struct_3.main.Eggs.Ham =
    {
        bread: i16 = 123
        butter: f32 = 1.2300000190734863
        nails: [2]i32 = [ 123, 123, ]
        cream: test_ret_anon_struct_3.main.Eggs.Ham.Milk =
        {
            cow: u8 = 123
            soy: i8 = 123
            almond: bool = true
        }
        more_cream: [2]test_ret_anon_struct_3.main.Eggs.Ham.Milk = [ { cow: u8 = 123, soy: i8 = 123, almond: bool = true, }, { cow: u8 = 123, soy: i8 = 123, almond: bool = true, }, ]
        nuts: [2][2]u8 = [ [ 123, 123, ], [ 123, 123, ], ]
    }
    bells: [3]u8 = [ 123, 123, 123, ]
    pointer: *u32 = u32@7ffd56d0fa8c
    optional: ?*u32 = -
}
4 Likes