Zig has no built-in print function or statement.
If you want to print something quickly, you can use std.debug.print
:
const std = @import("std");
pub fn main() void {
std.debug.print("Hello World!", .{});
}
Why is there no built-in function like C's printf
?
One answer is that, unlike C, Zig doesn't need to special-case the printing function. In C, printf
could not be implemented as a normal function without losing its special compile-time checks (i.e., the compiler can check that the number of arguments passed to printf
matches the number of %
placeholders in the format string).
In Zig all of that can be implemented in userland using comptime checks.
Another reason why Zig has no built-in print function is that printing is less obvious than what some other languages would lead you to believe.
When printing becomes actually important
When you are just printing a debug line to the console, you don't care about handling error conditions, but sometimes printing to the terminal is the core functionality of your application and at that point, to write a robust tool, you will need to design for failure.
On top of that there might be performance implications related to buffering, or you might be sharing the output stream with other threads, at which point a lock might (or might not) be necessary to ensure that the printed information keeps making sense.
Earlier we saw std.debug.print
and that function made some choices for us:
- it prints to stderr
- it has a lock in case you have multiple threads
- it does not buffer
- errors get discarded
This is its implementation (from the stdlib):
pub fn print(comptime fmt: []const u8, args: anytype) void {
const held = stderr_mutex.acquire();
defer held.release();
const stderr = io.getStdErr().writer();
nosuspend stderr.print(fmt, args) catch return;
}
Printing more reliably
To print in a more reliable way, you can use std.log
, which also works as a more complete logging system with support for different scopes and levels. You can read more about it in the stdlib.
Printing to stdout
If you want to have direct access to stdout, you can use std.io.getStdOut()
and from there use the writer
interface to print, just like the code above does with stderr. At that point it will be up to you if you want to have buffering (using a std.io.BufferedWriter
) or a lock, and you will also have to decide what to do with errors.
Watch the talk
If you want to listen to me give a full talk just on this topic, here you go :^)
Top comments (2)
Not true. Checks are not necessary, just gotta be careful.
I am new to Zig and new to this forum, but surprised to see no comments on this. As a long-time C programmer, I was very surprised that stdout (eg, std.io.getStdOut().writer()) is not buffered by default. After thinking about it for a few moments, I realize that this is a faulty mindset on my part caused by conditioning. Over the past few decades, the amount of confusion that has been caused by the different treatment of buffering in stdout vs. stderr has probably led to 100s of thousands of lost hours. (Whether those hours are "lost" or are worthwhile educational experiences is a different question!) Making this very explicit is an aspect of the Zig philosophy that I find very refreshing. Great talk!