Formatting, Printing... one obvious way to do things?

From origin (other languages) I am used to working with strings. With Zig things are less obvious for me.

How do we actually create strings or print?
We can:

  • use the disappearing std.BoundedArray for creating a (short) string.
  • use an ArrayListUnmanaged with heap allocation to create a string.
  • use format
  • use print
  • use the writer of ArrayList
  • and probably many more.

There also seems some compiler magic going on with a format function for our structs which looks like (got thus one from a video of DudeTheBuilder):

pub fn format(self: MyStruct, 
comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {}

Is that documented? I could not find anything on what is going on there.
What should the type of writer be? With anytype we don’t know the type and available functions (no code completion).

I would be grateful if someone smarter than me is able to disect my blurry view into “The One Obvious Way To Do It”.
And also “What is the fastest way to do things?”
(In my chess-case I have to output to stdout, which can be either a terminal or a relayed stdout by some GUI).

1 Like

After writergate “The One Obvious Way To Do It" is to just write to writer.

format method is used to convert type to a string by writing it to writer.
print is used to do string interpolation which intern calles format function on types if needed.
Generally you can only print, format to a Writer.
Writer allows you to write to a bunch of places including ArrayList, Terminal, GUI or anything else and this is the whole point of Writer.

Also format function is much nicer now:

3 Likes

It’s interesting because in the ‘printf’ Zig override I have in the sokol Zig bindings I had an (old-style) writer before, but now just replaced that with fmt.bufPrint into a buffer (that buffer is the exact same where the C vprintf() equivalent is doing it’s formatted printing to, so feature wise there’s no difference).

This is the new code:

(and before you mention it, that “putc” at the end is not the C stdlib putc but a very lightweight “copy this byte into a buffer” function - and it’s not different from what the old writer did).

The old writer implementation looked like this (needless to say, I like the bufPrint() version a lot more

Not compiler magic, just comptime magic :wink: From the std.fmt.format docs:

If a formatted user type contains a function of the type

pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void

with ? being the type formatted, this function will be called instead of the default implementation. This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
A user type may be a struct, vector, union or enum type.

The actual implementation of this is dead simple (in std.fmt.formatType):

if (std.meta.hasMethod(T, "format")) {
    return try value.format(actual_fmt, options, writer);
}

(Note that this is looking like it will move to std.Io.Writer.print in 0.15)

1 Like

also format options have been updated:

  • t for tag names of enums or tagged unions
  • b64 to display bytes/strings in base 64
  • D to output nanosecconds in a human readable format
  • or custom duration (formatDuration fn with same signature as custom format)
  • B to output bytes in human readable SI units (decimal)
  • Bi to output bytes in human readable IEC units (binary)
  • B and Bi accept custom format functions too formatByteSize with an additional ByteSizeUnits arg that can be comptime
  • f is now required to use a types custom format function

looks like most options can use a custom format fn with some variations in arguments, i got tired of listing them.

The format function is simpler now:
fn (*Writer, args: anytype) Writer.Error!void
though in the implementation it doesn’t pass it args, only the writer, so not sure what that’s about, got this from docs.

1 Like

Is there anything like Python’s __str__ and __repr__ which are used to format objects in a human -readable or (ideally) in a source-code like way?

I haven’t tried it, but it looks like two different string forms for a struct could be achieved with the old format function, but not with the new.

If that’s true, I don’t really care much, but it’s something to consider.

std.fmt.alt allows you to call format functions with different names (which aren’t called format).

3 Likes

Here’s the approach I am using in zignal:

The Image struct has a format method that outputs a human readable string. Something like:

Image(Rgba){ .rows = 1024, .cols = 1024 }

Image has also a display method, that returns a DisplayFormatter struct, which, in turn, has a format method. This method is in charge of displaying the image to the terminal, using the specified protocol (or auto detect the best supported one).

This allows me to do stuff like this:

const std = @import("std");
const zignal = @import("zignal");
const Image = zignal.Image(zignal.Rgba);

pub fn main() !void {
    var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
    defer _ = debug_allocator.deinit();
    const gpa = debug_allocator.allocator();

    var image: Image = try .load(gpa, "../assets/liza.jpg");
    defer image.deinit(gpa);

    std.debug.print("{f}\n", .{image});
    std.debug.print("{f}\n", .{image.display(.auto)});
}

Which outputs:

I got all this info from reading this PR:

2 Likes

Aha aha that clears things up!

I think it would be great to mark some features that will get future changes in the documentation itself. That way we can isolate those features and implement the changes in our personal projects.
Recently I had a tough time figuring out how to use bufferedWriter - finally found out that it was removed. For starters like me it may be little intimidating.
Of course changes are expected but just putting a tentative mark in the documentation will help a lot.
Anyway great language though - great speed !

Thank you

Reading the “writergate” of Andrew I get the feeling I need to start learning Zig all over again.
Anyway: thanks for all responses. I know a bit where to look now.
I think I will wait with programming any further until 0.15 is there.