Is 'x' format deprecated, or is it not?

Yesterday I tried playing with optionals, slices, etc., and ran into a strange thing I can’t explain.
Consider these two short examples:
Example 1:

const std = @import("std");

pub fn main() void
{
    std.debug.print("{x:2>0}\n", .{ 0xFE });
    std.debug.print("{x}\n", .{ 0xAB });
}

Works as expected -

zig run x_fmt.zig 
fe
ab

Example 2:

const std = @import("std");

pub fn main() void
{
    var a: ?[]const u8 = null;
    if (a) |a_val|
    {
        std.debug.print("a is not null = {x:8>0}\n", .{ a_val });
    }
}

Does not compile -

zig run x_fmt_deprecated.zig 
/home/archie/apps/zig/lib/std/fmt.zig:976:16: error: specifier 'x' has been deprecated, wrap your argument in std.fmt.fmtSliceHexLower instead
        'x' => @compileError("specifier 'x' has been deprecated, wrap your argument in std.fmt.fmtSliceHexLower instead"),
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/archie/apps/zig/lib/std/fmt.zig:655:50: note: called from here                                                                          
                            comptime checkTextFmt(actual_fmt);
                                     ~~~~~~~~~~~~^~~~~~~~~~~~
referenced by:                                                                                                                                
    format__anon_7150: /home/archie/apps/zig/lib/std/fmt.zig:184:23
    print__anon_4079: /home/archie/apps/zig/lib/std/io/writer.zig:28:34
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

Replacing

std.debug.print("a is not null = {x:8>0}\n", .{ a_val });

with

std.debug.print("a is not null = {s}\n", .{ std.fmt.fmtSliceHexLower(a_val) });

makes it compile, but doesn’t answer the question: how come example 1 works, and example 2 does not?

Is x / X format deprecated, on its way out, or is it gone already? If yes, how do I apply additional parameters, like fill, alignment, width, etc.?

Also, example 2 compiles with this std.debug.print statement:

std.debug.print("a is not null = {x:8>0}\n", .{ @intFromPtr(a_val.ptr) });

To clarify, example 2’s print statement may be incorrect, but my question is about the error message: specifier 'x' has been deprecated, wrap your argument in std.fmt.fmtSliceHexLower instead.

I believe that error message is in reference to how previous versions of Zig (I don’t recall from which version onwards this changed) used to let you use the x format specifier with a slice and produced the individual bytes of the slice as hex numbers. That’s no longer available (I presume to reduce confusion having one format specifier applying to two totally different types) and you can use std.fmt.fmtSliceHexLower for that.

As a siede note, if I’m deducing your format specifiers correctly, I believe yu have the width and padding values mixed up. Instead of 8>0 (pad with 8, right align, width 0?) it should be 0>8.

3 Likes

Ah, this is good to know. Having a nice, short, convenient x / X format, complete with padding and alignment is a blessing. Would be sad to see it go.

As a siede note, if I’m deducing your format specifiers correctly, I believe yu have the width and padding values mixed up. Instead of 8>0 (pad with 8, right align, width 0?) it should be 0>8.

You are correct, of course. I get them confused sometimes :man_shrugging:

1 Like