Hello World

hello.zig

  1. const std = @import("std");
  2. pub fn main() !void {
  3. const stdout = std.io.getStdOut().writer();
  4. try stdout.print("Hello, {s}!\n", .{"world"});
  5. }

Shell

  1. $ zig build-exe hello.zig
  2. $ ./hello
  3. Hello, world!

The Zig code sample above demonstrates one way to create a program that will output: Hello, world!.

The code sample shows the contents of a file named hello.zig. Files storing Zig source code are UTF-8 encoded text files. The files storing Zig source code must be named with the _.zig_ extension.

Following the hello.zig Zig code sample, the Zig Build System is used to build an executable program from the hello.zig source code. Then, the hello program is executed showing its output Hello, world!. The lines beginning with $ represent command line prompts and a command. Everything else is program output.

The code sample begins by adding the Zig Standard Library to the build using the @import builtin function. The @import("std") function call creates a structure that represents the Zig Standard Library. The code then declares a constant identifier, named std, that gives access to the features of the Zig Standard Library.

Next, a public function, pub fn, named main is declared. The main function is necessary because it tells the Zig compiler where the start of the program exists. Programs designed to be executed will need a pub fn main function.

For more advanced use cases, Zig offers other features to inform the compiler where the start of the program exists. Also, libraries do not need a pub fn main function because library code is called by other programs or libraries.

A function is a block of any number of statements and expressions that, as a whole, perform a task. Functions may or may not return data after they are done performing their task. If a function cannot perform its task, it might return an error. Zig makes all of this explicit.

In the hello.zig code sample, the main function is declared with the !void return type. This return type is known as an Error Union Type. This syntax tells the Zig compiler that the function will either return an error or a value. An error union type combines an Error Set Type and any other data type (e.g. a Primitive Type or a user-defined type such as a struct, enum, or union). The full form of an error union type is <error set type>``!``<any data type>. In the code sample, the error set type is not explicitly written on the left side of the ! operator. When written this way, the error set type is an inferred error set type. The void after the ! operator tells the compiler that the function will not return a value under normal circumstances (i.e. when no errors occur).

Note to experienced programmers: Zig also has the boolean operator !a where a is a value of type bool. Error union types contain the name of the type in the syntax: !``<any data type>.

In Zig, a function’s block of statements and expressions are surrounded by an open curly-brace { and close curly-brace }. Inside of the main function are expressions that perform the task of outputting Hello, world! to standard output.

First, a constant identifier, stdout, is initialized to represent standard output’s writer. Then, the program tries to print the Hello, world! message to standard output.

Functions sometimes need information to perform their task. In Zig, information is passed to functions between an open parenthesis ( and a close parenthesis ) placed after the function’s name. This information is also known as arguments. When there are multiple arguments passed to a function, they are separated by commas ,.

The two arguments passed to the stdout.print() function, "Hello, {s}!\n" and .{"world"}, are evaluated at compile-time. The code sample is purposely written to show how to perform string substitution in the print function. The curly-braces inside of the first argument are substituted with the compile-time known value inside of the second argument (known as a tuple). The \n inside of the double-quotes of the first argument is the escape sequence for the newline character. The try expression evaluates the result of stdout.print. If the result is an error, then the try expression will return from main with the error. Otherwise, the program will continue. In this case, there are no more statements or expressions left to execute in the main function, so the program exits.

In Zig, the standard output writer’s print function is allowed to fail because it is actually a function defined as part of a generic Writer. Consider a generic Writer that represents writing data to a file. When the disk is full, a write to the file will fail. However, we typically do not expect writing text to the standard output to fail. To avoid having to handle the failure case of printing to standard output, you can use alternate functions: the functions in std.log for proper logging or the std.debug.print function. This documentation will use the latter option to print to standard error (stderr) and silently return on failure. The next code sample, hello_again.zig demonstrates the use of std.debug.print.

hello_again.zig

  1. const print = @import("std").debug.print;
  2. pub fn main() void {
  3. print("Hello, world!\n", .{});
  4. }

Shell

  1. $ zig build-exe hello_again.zig
  2. $ ./hello_again
  3. Hello, world!

Note that you can leave off the ! from the return type because std.debug.print cannot fail.

See also: