diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd index f935d8b..9017cd4 100644 --- a/Chapters/01-zig-weird.qmd +++ b/Chapters/01-zig-weird.qmd @@ -20,7 +20,7 @@ knitr::opts_chunk$set( # Introducing Zig In this chapter, I want to introduce you to the world of Zig. -Despite it's rapidly growing over the last years, Zig is, still, a very young language^[New programming languages in general, take years and years to be developed.]. +Zig is a very young language that is being actively developed. As a consequence, it's world is still very wild and to be explored. This book is my attempt to help you on your personal journey for understanding and exploring the exciting world of Zig. @@ -31,13 +31,10 @@ So, if you have experience with Python, or Javascript, for example, is fine. But, if you do have experience with low-level languages, such as C, C++, or Rust, you will probably learn faster throughout this book. - - ## What is Zig? -Zig is a modern, low-level, and general-purpose programming language. Some programmers interpret -Zig as the "modern C language". It is a simple language like C, but with some -modern features. +Zig is a modern, low-level, and general-purpose programming language. Some programmers think of +Zig as a modern and better version of C. In the author's personal interpretation, Zig is tightly connected with "less is more". Instead of trying to become a modern language by adding more and more features, @@ -102,18 +99,13 @@ are created inside of your current directory. First, a "source" (`src`) director is created, containing two files, `main.zig` and `root.zig`. Each `.zig` file is a separate Zig module, which is simply a text file that contains some Zig code. +By convention, the `main.zig` module is where your main function lives. Thus, +if you are building an executable program in Zig, you need to declare a `main()` function, +which represents the entrypoint of your program, i.e. it is where the execution of your program begins. -The `main.zig` file for example, contains a `main()` function, which represents -the entrypoint of your program. It is where the execution of your program begins. -As you would expect from a C, C++, Rust or Go, -to build an executabe program in Zig, you also need to declare a `main()` function in your module. -So, the `main.zig` module represents an executable program written in Zig. - -On the other side, the `root.zig` module does not contain a `main()` function. Because -it represents a library written in Zig. Libraries are different than executables. -They don't need to have an entrypoint to work. -So, you can choose which file (`main.zig` or `root.zig`) you want to follow depending on which type -of project (executable or library) you want to develop. +However, if you are building a library (instead of an executable program), then, +the normal procedure is to delete this `main.zig` file and start with the `root.zig` module. +By convention, the `root.zig` module is the root source file of your library. ```bash tree . @@ -130,8 +122,7 @@ tree . 1 directory, 4 files ``` - -Now, in addition to the source directory, two other files were created in our working directory: +The `ìnit` command also creates two additional files in our working directory: `build.zig` and `build.zig.zon`. The first file (`build.zig`) represents a build script written in Zig. This script is executed when you call the `build` command from the `zig` compiler. In other words, this file contain Zig code that executes the necessary steps to build the entire project. @@ -167,35 +158,35 @@ So, everything you need to build a complex Zig project is the [^zig-build-system]: . -Now that we described this topic in more depth, let's focus -on the second generated file (`build.zig.zon`), which is the Zig package manager configuration file, +The second generated file (`build.zig.zon`) is the Zig package manager configuration file, where you can list and manage the dependencies of your project. Yes, Zig has a package manager (like `pip` in Python, `cargo` in Rust, or `npm` in Javascript) called Zon, and this `build.zig.zon` file is similar to the `package.json` file -in Javascript projects, or, the `Pipfile` file in Python projects, or the `Cargo.toml` file in Rust projects. +in Javascript projects, or, the `Pipfile` file in Python projects, +or the `Cargo.toml` file in Rust projects. -### Looking at the `root.zig` file {#sec-root-file} +### The file `root.zig` {#sec-root-file} -Let's take a look at the `root.zig` file, and start to analyze some of the -syntax of Zig. -The first thing that you might notice, is that every line of code -that have an expression in it, ends with a semicolon character (`;`). This is -similar syntax to other languages such as C, C++ and Rust, -which have the same rule. +Let's take a look into the `root.zig` file. +You might have notice that every line of code with an expression ends with a semicolon (`;`). +This follows the syntax of a C-family programming language[^c-family]. + +[^c-family]: Also, notice the `@import()` call at the first line. We use this built-in function to import functionality from other Zig modules into our current module. -In other words, the `@import()` function works similarly to the `#include` pre-processor +In other words, this `@import()` function works similarly to the `#include` pre-processor in C or C++, or, to the `import` statement in Python or Javascript code. In this example, we are importing the `std` module, -which gives you access to the Zig standard library. +which gives you access to the Zig Standard Library. In this `root.zig` file, we can also see how assignments (i.e. creating new objects) are made in Zig. You can create a new object in Zig by using the following syntax `(const|var) name = value;`. In the example below, we are creating two constant objects (`std` and `testing`). At @sec-assignments we talk more about objects in general. + ```{zig} #| eval: false const std = @import("std"); @@ -206,13 +197,15 @@ export fn add(a: i32, b: i32) i32 { } ``` -Functions in Zig are declared similarly to functions in Rust, using the `fn` keyword. In the example above, -we are declaring a function called `add()`, which have two arguments named `a` and `b`, and returns -a integer number (`i32`) as result. +Functions in Zig are declared using the `fn` keyword. +In this `root.zig` module, we are declaring a function called `add()`, which has two arguments named `a` and `b`. +The function returns an integer of the type `i32` as result. -Maybe Zig is not exactly a strongly-typed language, because you do not need -necessarily to specify the type of every single object you create across your source code. -But you do have to explicitly specify the type of every function argument, and also, + +Zig is not exactly a strongly-typed language. Because you can (if you want to) omit +the type of an object in your code, if this type can be derived from the assigned value. +But there are other situations where you do need to be explicit. +For example, you do have to explicitly specify the type of every single function argument, and also, the return type of every function you create in Zig. So, at least in function declarations, Zig is a strongly-typed language. @@ -224,15 +217,14 @@ the syntax in Zig is identical to the syntax in Rust, which also specifies types using the colon character. Lastly, we have the return type of the function at the end of the line, before we open -the curly braces to start writing the function's body, which, in the example above is -again a signed 32 bit integer (`i32`) value. This specific part is different than it is in Rust. -Because in Rust, the return type of a function is specified after an arrow (`->`). -While in Zig, we simply declare the return type directly after the parentheses with the function arguments. +the curly braces to start writing the function's body. In the example above, this type is also +a signed 32 bit integer (`i32`) value. -We also have an `export` keyword before the function declaration. This keyword +Notice that we also have an `export` keyword before the function declaration. This keyword is similar to the `extern` keyword in C. It exposes the function to make it available in the library API. + In other words, if you have a project where you are currently building a library for other people to use, you need to expose your functions so that they are available in the library's API, so that users can use it. @@ -242,11 +234,11 @@ by the `zig` compiler. Having that in mind, the keyword `export` is a keyword used in libraries written in Zig. -So, if you are not currently writing a library in your project, then, you do not need to +If you are not currently writing a library in your project, then, you do not need to care about this keyword. -### Looking at the `main.zig` file {#sec-main-file} +### The `main.zig` file {#sec-main-file} Now that we have learned a lot about Zig's syntax from the `root.zig` file, let's take a look at the `main.zig` file. diff --git a/_freeze/Chapters/01-zig-weird/execute-results/html.json b/_freeze/Chapters/01-zig-weird/execute-results/html.json index d9cc8a5..9704487 100644 --- a/_freeze/Chapters/01-zig-weird/execute-results/html.json +++ b/_freeze/Chapters/01-zig-weird/execute-results/html.json @@ -1,8 +1,8 @@ { - "hash": "27b634b16b3c3fc42493ea4fabf15dd4", + "hash": "c879ea1c825dd62d2d7c4fa3604aa5db", "result": { "engine": "knitr", - "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n\n# Introducing Zig\n\nIn this chapter, I want to introduce you to the world of Zig.\nDespite it's rapidly growing over the last years, Zig is, still, a very young language^[New programming languages in general, take years and years to be developed.].\nAs a consequence, it's world is still very wild and to be explored.\nThis book is my attempt to help you on your personal journey for\nunderstanding and exploring the exciting world of Zig.\n\nI assume you have previous experience with some programming\nlanguage in this book, not necessarily with a low-level one.\nSo, if you have experience with Python, or Javascript, for example, is fine.\nBut, if you do have experience with low-level languages, such as C, C++, or\nRust, you will probably learn faster throughout this book.\n\n\n\n## What is Zig?\n\nZig is a modern, low-level, and general-purpose programming language. Some programmers interpret\nZig as the \"modern C language\". It is a simple language like C, but with some\nmodern features.\n\nIn the author's personal interpretation, Zig is tightly connected with \"less is more\".\nInstead of trying to become a modern language by adding more and more features,\nmany of the core improvements that Zig brings to the\ntable are actually about removing annoying and evil behaviours/features from C and C++.\nIn other words, Zig tries to be better by simplifying the language, and by having more consistent and robust behaviour.\nAs a result, analyzing, writing and debugging applications become much easier and simpler in Zig, than it is in C or C++.\n\nThis philosophy becomes clear with the following phrase from the official website of Zig:\n\n> \"Focus on debugging your application rather than debugging your programming language knowledge\".\n\nThis phrase is specially true for C++ programmers. Because C++ is a gigantic language,\nwith tons of features, and also, there are lots of different \"flavors of C++\". These elements\nare what makes C++ so much complex and hard to learn. Zig tries to go in the opposite direction.\nZig is a very simple language, more closely related to other simple languages such as C and Go.\n\nThe phrase above is still important for C programmers too. Because, even C being a simple\nlanguage, it is still hard sometimes to read and understand C code. For example, pre-processor macros in\nC are an evil source of confusion. They really makes it hard sometimes to debug\nC programs. Because macros are essentially a second language embedded in C that obscures\nyour C code. With macros, you are no longer 100% sure about which pieces\nof code are being sent to the compiler. It obscures the actual source code that you wrote.\n\nYou don't have macros in Zig. In Zig, the code you write, is the actual code that get's compiled by the compiler.\nYou don't have evil features that obscures you code.\nYou also don't have hidden control flow happening behind the scenes. And, you also\ndon't have functions or operators from the standard library that make\nhidden memory allocations behind your back.\n\nBy being a simpler language, Zig becomes much more clear and easier to read/write,\nbut at the same time, it also achieves a much more robust state, with more consistent\nbehaviour in edge situations. Once again, less is more.\n\n\n## Hello world in Zig\n\nWe begin our journey in Zig by creating a small \"Hello World\" program.\nTo start a new Zig project in your computer, you simply call the `init` command\nfrom the `zig` compiler.\nJust create a new directory in your computer, then, init a new Zig project\ninside this directory, like this:\n\n```bash\nmkdir hello_world\ncd hello_world\nzig init\n```\n\n```\ninfo: created build.zig\ninfo: created build.zig.zon\ninfo: created src/main.zig\ninfo: created src/root.zig\ninfo: see `zig build --help` for a menu of options\n```\n\n### Understanding the project files {#sec-project-files}\n\nAfter you run the `init` command from the `zig` compiler, some new files\nare created inside of your current directory. First, a \"source\" (`src`) directory\nis created, containing two files, `main.zig` and `root.zig`. Each `.zig` file\nis a separate Zig module, which is simply a text file that contains some Zig code.\n\n\nThe `main.zig` file for example, contains a `main()` function, which represents\nthe entrypoint of your program. It is where the execution of your program begins.\nAs you would expect from a C, C++, Rust or Go,\nto build an executabe program in Zig, you also need to declare a `main()` function in your module.\nSo, the `main.zig` module represents an executable program written in Zig.\n\nOn the other side, the `root.zig` module does not contain a `main()` function. Because\nit represents a library written in Zig. Libraries are different than executables.\nThey don't need to have an entrypoint to work.\nSo, you can choose which file (`main.zig` or `root.zig`) you want to follow depending on which type\nof project (executable or library) you want to develop.\n\n```bash\ntree .\n```\n\n```\n.\n├── build.zig\n├── build.zig.zon\n└── src\n ├── main.zig\n └── root.zig\n\n1 directory, 4 files\n```\n\n\nNow, in addition to the source directory, two other files were created in our working directory:\n`build.zig` and `build.zig.zon`. The first file (`build.zig`) represents a build script written in Zig.\nThis script is executed when you call the `build` command from the `zig` compiler.\nIn other words, this file contain Zig code that executes the necessary steps to build the entire project.\n\nIn general, low-level languages normally use a compiler to build your\nsource code into binary executables or binary libraries.\nNevertheless, this process of compiling your source code and building\nbinary executables or binary libraries from it, became a real challenge\nin the programming world, once the projects became bigger and bigger.\nAs a result, programmers created \"build systems\", which are a second set of tools designed to make this process\nof compiling and building complex projects, easier.\n\nExamples of build systems are CMake, GNU Make, GNU Autoconf and Ninja,\nwhich are used to build complex C and C++ projects.\nWith these systems, you can write scripts, which are called \"build scripts\".\nThey simply are scripts that describes the necessary steps to compile/build\nyour project.\n\nHowever, these are separate tools, that do not\nbelong to C/C++ compilers, like `gcc` or `clang`.\nAs a result, in C/C++ projects, you have not only to install and\nmanage your C/C++ compilers, but you also have to install and manage\nthese build systems separately.\n\nBut instead of using a separate build system, in Zig, we use the\nZig language itself to write build scripts.\nIn other words, Zig contains a native build system in it. And\nwe can use this build system to write small scripts in Zig,\nwhich describes the necessary steps to build/compile our Zig project[^zig-build-system].\nSo, everything you need to build a complex Zig project is the\n`zig` compiler, and nothing more.\n\n[^zig-build-system]: .\n\n\nNow that we described this topic in more depth, let's focus\non the second generated file (`build.zig.zon`), which is the Zig package manager configuration file,\nwhere you can list and manage the dependencies of your project. Yes, Zig has\na package manager (like `pip` in Python, `cargo` in Rust, or `npm` in Javascript) called Zon,\nand this `build.zig.zon` file is similar to the `package.json` file\nin Javascript projects, or, the `Pipfile` file in Python projects, or the `Cargo.toml` file in Rust projects.\n\n\n### Looking at the `root.zig` file {#sec-root-file}\n\nLet's take a look at the `root.zig` file, and start to analyze some of the\nsyntax of Zig.\nThe first thing that you might notice, is that every line of code\nthat have an expression in it, ends with a semicolon character (`;`). This is\nsimilar syntax to other languages such as C, C++ and Rust,\nwhich have the same rule.\n\nAlso, notice the `@import()` call at the first line. We use this built-in function\nto import functionality from other Zig modules into our current module.\nIn other words, the `@import()` function works similarly to the `#include` pre-processor\nin C or C++, or, to the `import` statement in Python or Javascript code.\nIn this example, we are importing the `std` module,\nwhich gives you access to the Zig standard library.\n\nIn this `root.zig` file, we can also see how assignments (i.e. creating new objects)\nare made in Zig. You can create a new object in Zig by using the following syntax\n`(const|var) name = value;`. In the example below, we are creating two constant\nobjects (`std` and `testing`). At @sec-assignments we talk more about objects in general.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst testing = std.testing;\n\nexport fn add(a: i32, b: i32) i32 {\n return a + b;\n}\n```\n:::\n\n\n\n\nFunctions in Zig are declared similarly to functions in Rust, using the `fn` keyword. In the example above,\nwe are declaring a function called `add()`, which have two arguments named `a` and `b`, and returns\na integer number (`i32`) as result.\n\nMaybe Zig is not exactly a strongly-typed language, because you do not need\nnecessarily to specify the type of every single object you create across your source code.\nBut you do have to explicitly specify the type of every function argument, and also,\nthe return type of every function you create in Zig. So, at least in function declarations,\nZig is a strongly-typed language.\n\nWe specify the type of an object or a function argument in Zig, by\nusing a colon character (`:`) followed by the type after the name of this object/function argument.\nWith the expressions `a: i32` and `b: i32`, we know that, both `a` and `b` arguments have type `i32`,\nwhich is a signed 32 bit integer. In this part,\nthe syntax in Zig is identical to the syntax in Rust, which also specifies types by\nusing the colon character.\n\nLastly, we have the return type of the function at the end of the line, before we open\nthe curly braces to start writing the function's body, which, in the example above is\nagain a signed 32 bit integer (`i32`) value. This specific part is different than it is in Rust.\nBecause in Rust, the return type of a function is specified after an arrow (`->`).\nWhile in Zig, we simply declare the return type directly after the parentheses with the function arguments.\n\nWe also have an `export` keyword before the function declaration. This keyword\nis similar to the `extern` keyword in C. It exposes the function\nto make it available in the library API.\n\nIn other words, if you have a project where you are currently building\na library for other people to use, you need to expose your functions\nso that they are available in the library's API, so that users can use it.\nIf we removed the `export` keyword from the `add()` function declaration,\nthen, this function would be no longer exposed in the library object built\nby the `zig` compiler.\n\n\nHaving that in mind, the keyword `export` is a keyword used in libraries written in Zig.\nSo, if you are not currently writing a library in your project, then, you do not need to\ncare about this keyword.\n\n\n### Looking at the `main.zig` file {#sec-main-file}\n\nNow that we have learned a lot about Zig's syntax from the `root.zig` file,\nlet's take a look at the `main.zig` file.\nA lot of the elements we saw in `root.zig` are also present in `main.zig`.\nBut we have some other elements that we did not have seen yet, so let's dive in.\n\nFirst, look at the return type of the `main()` function in this file.\nWe can see a small change. Now, the return\ntype of the function (`void`) is accompanied by an exclamation mark (`!`).\nWhat this exclamation mark is telling us, is that this `main()` function\nmight also return an error.\n\nSo, in this example, the `main()` function can either return `void`, or, return an error.\nThis is an interesting feature of Zig. If you write a function, and, something inside of\nthe body of this function might return an error, then, you are forced to:\n\n- either add the exclamation mark to the return type of the function, to make it clear that\nthis function might return an error.\n- or explicitly handle this error that might occur inside the function, to make sure that,\nif this error does happen, you are prepared, and your function will no longer return an error\nbecause you handled the error inside your function.\n\nIn most programming languages, we normally handle (or deals with) an error through\na *try catch* pattern, and Zig, this is no different. But, if we look at the `main()` function\nbelow, you can see that we do have a `try` keyword in the 5th line. But we do not have a `catch` keyword\nin this code.\n\nThis means that, we are using the keyword `try` to execute a code that might return an error,\nwhich is the `stdout.print()` expression. But because we do not have a `catch` keyword in this line,\nwe are not treating (or dealing with) this error.\nSo, if this expression do return an error, we are not catching and solving this error in any way.\nThat is why the exclamation mark was added to the return type of the function.\n\nSo, in essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword do nothing essentially. It simply passes this value forward. But, if the expression do\nreturn an error, then, the `try` keyword will unwrap and return this error from the function, and also print it's\nstack trace to `stderr`.\n\nThis might sound weird to you, if you come from a high-level language. Because in\nhigh-level languages, such as Python, if an error occurs somewhere, this error is automatically\nreturned and the execution of your program will automatically stops, even if you don't want\nto stop the execution. You are obligated to face the error.\n\nBut if you come from a low-level language, then, maybe, this idea do not sound so weird or distant to you.\nBecause in C for example, normally functions doesn't raise errors, or, they normally don't stop the execution.\nIn C, error handling\nis done by constantly checking the return value of the function. So, you run the function,\nand then, you use an if statement to check if the function returned a value that is valid,\nor, if it returned an error. If an error was returned from the function, then, the if statement\nwill execute some code that fixes this error.\n\nSo, at least for C programmers, they do need to write a lot of if statements to\nconstantly check for errors around their code. And because of that, this simple feature from Zig, might be\nextraordinary for them. Because this `try` keyword can automatically unwrap the error,\nand warn you about this error, and let you deal with it, without any extra work from the programmer.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n```\n:::\n\n\n\n\nNow, another thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. This keyword means\n\"public\". It marks the `main()` function as a *public function* from this module.\n\nIn other words, every function that you declare in your Zig module is, by default, a private (or \"static\")\nfunction that belongs to this Zig module, and can only be used (or called) from within this same module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\nThis means that the `pub` keyword in Zig do essentially the opposite of what the `static` keyword\ndo in C/C++.\n\nBy making a function \"public\", you allow other Zig modules to access and call this function,\nand use it for they own purposes.\nall these other Zig modules need to do is, to import your module with the `@import()`\nbuilt-in function. Then, they get access to all public functions that are present in\nyour Zig module.\n\n\n### Compiling your source code {#sec-compile-code}\n\nYou can compile your Zig modules into a binary executable by running the `build-exe` command\nfrom the `zig` compiler. You simply list all the Zig modules that you want to build after\nthe `build-exe` command, separated by spaces. In the example below, we are compiling the module `main.zig`.\n\n```bash\nzig build-exe src/main.zig\n```\n\nSince we are building an executable, the `zig` compiler will look for a `main()` function\ndeclared in any of the files that you list after the `build-exe` command. If\nthe compiler does not find a `main()` function declared somewhere, a\ncompilation error will be raised, warning about this mistake.\n\nThe `zig` compiler also offers a `build-lib` and `build-obj` commands, which work\nthe exact same way as the `build-exe` command. The only difference is that, they compile your\nZig modules into a portale C ABI library, or, into object files, respectively.\n\nIn the case of the `build-exe` command, a binary executable file is created by the `zig`\ncompiler in the root directory of your project.\nIf we take a look now at the contents of our current directory, with a simple `ls` command, we can\nsee the binary file called `main` that was created by the compiler.\n\n```bash\nls\n```\n\n```\nbuild.zig build.zig.zon main src\n```\n\nIf I execute this binary executable, I get the \"Hello World\" message in the terminal\n, as we expected.\n\n```bash\n./main\n```\n\n```\nHello, world!\n```\n\n\n### Compile and execute at the same time {#sec-compile-run-code}\n\nOn the previous section, I presented the `zig build-exe` command, which\ncompiles Zig modules into an executable file. However, this means that,\nin order to execute the executable file, we have to run two different commands.\nFirst, the `zig build-exe` command, and then, we call the executable file\ncreated by the compiler.\n\nBut what if we wanted to perform these two steps,\nall at once, in a single command? We can do that by using the `zig run`\ncommand.\n\n```bash\nzig run src/main.zig\n```\n\n```\nHello, world!\n```\n\n### Compiling the entire project {#sec-compile-project}\n\nJust as I described at @sec-project-files, as our project grows in size and\ncomplexity, we usually prefer to organize the compilation and build process\nof the project into a build script, using some sort of \"build system\".\n\nIn other words, as our project grows in size and complexity,\nthe `build-exe`, `build-lib` and `build-obj` commands become\nharder to use directly. Because then, we start to list\nmultiple and multiple modules at the same time. We also\nstart to add built-in compilation flags to customize the\nbuild process for our needs, etc. It becomes a lot of work\nto write the necessary commands by hand.\n\nIn C/C++ projects, programmers normally opt to use CMake, Ninja, `Makefile` or `configure` scripts\nto organize this process. However, in Zig, we have a native build system in the language itself.\nSo, we can write build scripts in Zig to compile and build Zig projects. Then, all we\nneed to do, is to call the `zig build` command to build our project.\n\nSo, when you execute the `zig build` command, the `zig` compiler will search\nfor a Zig module named `build.zig` inside your current directory, which\nshould be your build script, containing the necessary code to compile and\nbuild your project. If the compiler do find this `build.zig` file in your directory,\nthen, the compiler will essentially execute a `zig run` command\nover this `build.zig` file, to compile and execute this build\nscript, which in turn, will compile and build your entire project.\n\n\n```bash\nzig build\n```\n\n\nAfter you execute this \"build project\" command, a `zig-out` directory\nis created in the root of your project directory, where you can find\nthe binary executables and libraries created from your Zig modules\naccordingly to the build commands that you specified at `build.zig`.\nWe will talk more about the build system in Zig latter in this book.\n\nIn the example below, I'm executing the binary executable\nnamed `hello_world` that was generated by the compiler after the\n`zig build` command.\n\n```bash\n./zig-out/bin/hello_world\n```\n\n```\nHello, world!\n```\n\n\n\n## How to learn Zig?\n\nWhat are the best strategies to learn Zig? \nFirst of all, of course this book will help you a lot on your journey through Zig.\nBut you will also need some extra resources if you want to be really good at Zig.\n\nAs a first tip, you can join a community with Zig programmers to get some help\n, when you need it:\n\n- Reddit forum: ;\n- Ziggit community: ;\n- Discord, Slack, Telegram, and others: ;\n\nNow, one of the best ways to learn Zig is to simply read Zig code. Try\nto read Zig code often, and things will become more clear.\nA C/C++ programmer would also probably give you this same tip.\nBecause this strategy really works!\n\nNow, where you can find Zig code to read?\nI personally think that, the best way of reading Zig code is to read the source code of the\nZig Standard Library. The Zig Standard Library is available at the [`lib/std` folder](https://github.com/ziglang/zig/tree/master/lib/std)[^zig-lib-std] on\nthe official GitHub repository of Zig. Access this folder, and start exploring the Zig modules.\n\nAlso, a great alternative is to read code from other large Zig\ncodebases, such as:\n\n1. the [Javascript runtime Bun](https://github.com/oven-sh/bun)[^bunjs].\n1. the [game engine Mach](https://github.com/hexops/mach)[^mach].\n1. a [LLama 2 LLM model implementation in Zig](https://github.com/cgbur/llama2.zig/tree/main)[^ll2].\n1. the [financial transactions database `tigerbeetle`](https://github.com/tigerbeetle/tigerbeetle)[^tiger].\n1. the [command-line arguments parser `zig-clap`](https://github.com/Hejsil/zig-clap)[^clap].\n1. the [UI framework `capy`](https://github.com/capy-ui/capy)[^capy].\n1. the [Language Protocol implementation for Zig, `zls`](https://github.com/zigtools/zls)[^zls].\n1. the [event-loop library `libxev`](https://github.com/mitchellh/libxev)[^xev].\n\n[^xev]: \n[^zls]: \n[^capy]: \n[^clap]: \n[^tiger]: \n[^ll2]: \n[^mach]: \n[^bunjs]: .\n\nAll these assets are available on GitHub,\nand this is great, because we can use the GitHub search bar in our advantage,\nto find Zig code that fits our description.\nFor example, you can always include `lang:Zig` in the GitHub search bar when you\nare searching for a particular pattern. This will limit the search to only Zig modules.\n\n[^zig-lib-std]: \n\nAlso, a great alternative is to consult online resources and documentations.\nHere is a quick list of resources that I personally use from time to time to learn\nmore about the language each day:\n\n- Zig Language Reference: ;\n- Zig Standard Library Reference: ;\n- Zig Guide: ;\n- Karl Seguin Blog: ;\n- Zig News: ;\n- Read the code written by one of the Zig core team members: ;\n- Some livecoding sessions are transmitted in the Zig Showtime Youtube Channel: ;\n\n\nAnother great strategy to learn Zig, or honestly, to learn any language you want,\nis to practice it by solving exercises. For example, there is a famous repository\nin the Zig community called [Ziglings](https://codeberg.org/ziglings/exercises/)[^ziglings]\n, which contains more than 100 small exercises that you can solve. It is a repository of\ntiny programs written in Zig that are currently broken, and your responsibility is to\nfix these programs, and make them work again.\n\n[^ziglings]: .\n\nA famous tech YouTuber known as *The Primeagen* also posted some videos (at YouTube)\nwhere he solves these exercises from Ziglings. The first video is named\n[\"Trying Zig Part 1\"](https://www.youtube.com/watch?v=OPuztQfM3Fg&t=2524s&ab_channel=TheVimeagen)[^prime1].\n\n[^prime1]: .\n\nAnother great alternative, is to solve the [Advent of Code exercises](https://adventofcode.com/)[^advent-code].\nThere are people that already took the time to learn and solve the exercises, and they posted\ntheir solutions on GitHub as well, so, in case you need some resource to compare while solving\nthe exercises, you can look at these two repositories:\n\n- ;\n- ;\n\n[^advent-code]: \n\n\n\n\n\n\n## Creating new objects in Zig (i.e. identifiers) {#sec-assignments}\n\nLet's talk more about objects in Zig. Readers that have past experience\nwith other programming languages might know this concept through\na different name, such as: \"variable\" or \"identifier\". In this book, I choose\nto use the term \"object\" to refer to this concept.\n\nTo create a new object (or a new \"identifier\") in Zig, we use\nthe keywords `const` or `var`. These keywords specificy if the object\nthat you are creating is mutable or not.\nIf you use `const`, then the object you are\ncreating is a constant (or immutable) object, which means that once you declare this object, you\ncan no longer change the value stored inside this object.\n\nOn the other side, if you use `var`, then, you are creating a variable (or mutable) object.\nYou can change the value of this object as many times you want. Using the\nkeyword `var` in Zig is similar to using the keywords `let mut` in Rust.\n\n### Constant objects vs variable objects\n\nIn the code example below, we are creating a new constant object called `age`.\nThis object stores a number representing the age of someone. However, this code example\ndoes not compiles succesfully. Because on the next line of code, we are trying to change the value\nof the object `age` to 25.\n\nThe `zig` compiler detects that we are trying to change\nthe value of an object/identifier that is constant, and because of that,\nthe compiler will raise a compilation error, warning us about the mistake.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 24;\n// The line below is not valid!\nage = 25;\n```\n:::\n\n\n\n\n```\nt.zig:10:5: error: cannot assign to constant\n age = 25;\n ~~^~~\n```\n\nIn contrast, if you use `var`, then, the object created is a variable object.\nWith `var` you can declare this object in your source code, and then,\nchange the value of this object how many times you want over future points\nin your source code.\n\nSo, using the same code example exposed above, if I change the declaration of the\n`age` object to use the `var` keyword, then, the program gets compiled succesfully.\nBecause now, the `zig` compiler detects that we are changing the value of an\nobject that allows this behaviour, because it is an \"variable object\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\n:::\n\n\n\n\n\n### Declaring without an initial value\n\nBy default, when you declare a new object in Zig, you must give it\nan initial value. In other words, this means\nthat we have to declare, and, at the same time, initialize every object we\ncreate in our source code.\n\nOn the other hand, you can, in fact, declare a new object in your source code,\nand not give it an explicit value. But we need to use a special keyword for that,\nwhich is the `undefined` keyword.\n\nIs important to emphasize that, you should avoid using `undefined` as much as possible.\nBecause when you use this keyword, you leave your object uninitialized, and, as a consequence,\nif for some reason, your code use this object while it is uninitialized, then, you will definitely\nhave undefined behaviour and major bugs in your program.\n\nIn the example below, I'm declaring the `age` object again. But this time,\nI do not give it an initial value. The variable is only initialized at\nthe second line of code, where I store the number 25 in this object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\n:::\n\n\n\n\nHaving these points in mind, just remember that you should avoid as much as possible to use `undefined` in your code.\nAlways declare and initialize your objects. Because this gives you much more safety in your program.\nBut in case you really need to declare an object without initializing it... the\n`undefined` keyword is the way to do it in Zig.\n\n\n### There is no such thing as unused objects\n\nEvery object (being constant or variable) that you declare in Zig **must be used in some way**. You can give this object\nto a function call, as a function argument, or, you can use it in another expression\nto calculate the value of another object, or, you can call a method that belongs to this\nparticular object. \n\nIt doesn't matter in which way you use it. As long as you use it.\nIf you try to break this rule, i.e. if your try to declare a object, but not use it,\nthe `zig` compiler will not compile your Zig source code, and it will issue a error\nmessage warning that you have unused objects in your code.\n\nLet's demonstrate this with an example. In the source code below, we declare a constant object\ncalled `age`. If you try to compile a simple Zig program with this line of code below,\nthe compiler will return an error as demonstrated below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\n:::\n\n\n\n\n```\nt.zig:4:11: error: unused local constant\n const age = 15;\n ^~~\n```\n\nEverytime you declare a new object in Zig, you have two choices:\n\n1. you either use the value of this object;\n2. or you explicitly discard the value of the object;\n\nTo explicitly discard the value of any object (constant or variable), all you need to do is to assign\nthis object to an special character in Zig, which is the underscore (`_`).\nWhen you assign an object to a underscore, like in the example below, the `zig` compiler will automatically\ndiscard the value of this particular object.\n\nYou can see in the example below that, this time, the compiler did not\ncomplain about any \"unused constant\", and succesfully compiled our source code.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// It compiles!\nconst age = 15;\n_ = age;\n```\n:::\n\n\n\n\nNow, remember, everytime you assign a particular object to the underscore, this object\nis essentially destroyed. It is discarded by the compiler. This means that you can no longer\nuse this object further in your code. It doesn't exist anymore.\n\nSo if you try to use the constant `age` in the example below, after we discarded it, you\nwill get a loud error message from the compiler (talking about a \"pointless discard\")\nwarning you about this mistake.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// It does not compile.\nconst age = 15;\n_ = age;\n// Using a discarded value!\nstd.debug.print(\"{d}\\n\", .{age + 2});\n```\n:::\n\n\n\n\n```\nt.zig:7:5: error: pointless discard\n of local constant\n```\n\n\nThis same rule applies to variable objects. Every variable object must also be used in\nsome way. And if you assign a variable object to the underscore,\nthis object also get's discarded, and you can no longer use this object.\n\n\n\n### You must mutate every variable objects\n\nEvery variable object that you create in your source code must be mutated at some point.\nIn other words, if you declare an object as a variable\nobject, with the keyword `var`, and you do not change the value of this object\nat some point in the future, the `zig` compiler will detect this,\nand it will raise an error warning you about this mistake.\n\nThe concept behind this is that every object you create in Zig should be preferably a\nconstant object, unless you really need an object whose value will\nchange during the execution of your program.\n\nSo, if I try to declare a variable object such as `where_i_live` below,\nand I do not change the value of this object in some way,\nthe `zig` compiler raises an error message with the phrase \"variable is never mutated\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\n:::\n\n\n\n\n```\nt.zig:7:5: error: local variable is never mutated\nt.zig:7:5: note: consider using 'const'\n```\n\n## Primitive Data Types {#sec-primitive-data-types}\n\nZig have many different primitive data types available for you to use.\nYou can see the full list of available data types at the official\n[Language Reference page](https://ziglang.org/documentation/master/#Primitive-Types)[^lang-data-types].\n\n[^lang-data-types]: .\n\nBut here is a quick list:\n\n- Unsigned integers: `u8`, 8-bit integer; `u16`, 16-bit integer; `u32`, 32-bit integer; `u64`, 64-bit integer; `u128`, 128-bit integer.\n- Signed integers: `i8`, 8-bit integer; `i16`, 16-bit integer; `i32`, 32-bit integer; `i64`, 64-bit integer; `i128`, 128-bit integer.\n- Float number: `f16`, 16-bit floating point; `f32`, 32-bit floating point; `f64`, 64-bit floating point; `f128`, 128-bit floating point;\n- Boolean: `bool`, represents true or false values.\n- C ABI compatible types: `c_long`, `c_char`, `c_short`, `c_ushort`, `c_int`, `c_uint`, and many others.\n- Pointer sized integers: `isize` and `usize`.\n\n\n\n\n\n\n\n## Arrays {#sec-arrays}\n\nYou create arrays in Zig by using a syntax that resembles the C syntax.\nFirst, you specify the size of the array (i.e. the number of elements that will be stored in the array)\nyou want to create inside a pair of brackets.\n\nThen, you specify the data type of the elements that will be stored inside this array.\nAll elements present in an array in Zig must have the same data type. For example, you cannot mix elements\nof type `f32` with elements of type `i32` in the same array.\n\nAfter that, you simply list the values that you want to store in this array inside\na pair of curly braces.\nIn the example below, I am creating two constant objets that contain different arrays.\nThe first object contains an array of 4 integer values, while the second object,\nan array of 3 floating point values.\n\nNow, you should notice that in the object `ls`, I am\nnot explicitly specifying the size of the array inside of the brackets. Instead\nof using a literal value (like the value 4 that I used in the `ns` object), I am\nusing the special character underscore (`_`). This syntax tells the `zig` compiler\nto fill this field with the number of elements listed inside of the curly braces.\nSo, this syntax `[_]` is for lazy (or smart) programmers who leave the job of\ncounting how many elements there are in the curly braces for the compiler.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst ls = [_]f64{432.1, 87.2, 900.05};\n_ = ns; _ = ls;\n```\n:::\n\n\n\n\nIs worth noting that these are static arrays, meaning that\nthey cannot grow in size.\nOnce you declare your array, you cannot change the size of it.\nThis is very commom in low level languages.\nBecause low level languages normally wants to give you (the programmer) full control over memory,\nand the way in which arrays are expanded is tightly related to\nmemory management.\n\n\n### Selecting elements of the array\n\nOne very commom activity is to select specific portions of an array\nyou have in your source code.\nIn Zig, you can select a specific element from your\narray, by simply providing the index of this particular\nelement inside brackets after the object name.\nIn the example below, I am selecting the third element from the\n`ns` array. Notice that Zig is a \"zero-index\" based language,\nlike C, C++, Rust, Python, and many other languages.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\ntry stdout.print(\"{d}\\n\", .{ ns[2] });\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n12\n```\n\n\n:::\n:::\n\n\n\n\nIn contrast, you can also select specific slices (or sections) of your array, by using a\nrange selector. Some programmers also call these selectors of \"slice selectors\",\nand they also exist in Rust, and have the exact same syntax as in Zig.\nAnyway, a range selector is a special expression in Zig that defines\na range of indexes, and it have the syntax `start..end`.\n\nIn the example below, at the second line of code,\nthe `sl` object stores a slice (or a portion) of the\n`ns` array. More precisely, the elements at index 1 and 2\nin the `ns` array. \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst sl = ns[1..3];\n_ = sl;\n```\n:::\n\n\n\n\nWhen you use the `start..end` syntax,\nthe \"end tail\" of the range selector is non-inclusive,\nmeaning that, the index at the end is not included in the range that is\nselected from the array.\nTherefore, the syntax `start..end` actually means `start..end - 1` in practice.\n\nYou can for example, create a slice that goes from the first to the\nlast elements of the array, by using `ar[0..ar.len]` syntax\nIn other words, it is a slice that\naccess all elements in the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ar = [4]u8{48, 24, 12, 6};\nconst sl = ar[0..ar.len];\n_ = sl;\n```\n:::\n\n\n\n\nYou can also use the syntax `start..` in your range selector.\nWhich tells the `zig` compiler to select the portion of the array\nthat begins at the `start` index until the last element of the array.\nIn the example below, we are selecting the range from index 1\nuntil the end of the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst sl = ns[1..];\n_ = sl;\n```\n:::\n\n\n\n\n\n### More on slices\n\nAs we discussed before, in Zig, you can select specific portions of an existing\narray. This is called *slicing* in Zig [@zigguide], because when you select a portion\nof an array, you are creating a slice object from that array.\n\nA slice object is essentially a pointer object accompained by a length number.\nThe pointer object points to the first element in the slice, and the\nlength number tells the `zig` compiler how many elements there are in this slice.\n\n> Slices can be thought of as a pair of `[*]T` (the pointer to the data) and a `usize` (the element count) [@zigguide].\n\nThrough the pointer contained inside the slice you can access the elements (or values)\nthat are inside this range (or portion) that you selected from the original array.\nBut the length number (which you can access through the `len` property of your slice object)\nis the really big improvement (over C arrays for example) that Zig brings to the table here.\n\nBecause with this length number\nthe `zig` compiler can easily check if you are trying to access an index that is out of the bounds of this particular slice,\nor, if you are causing any buffer overflow problems. In the example below,\nwe access the `len` property of the slice `sl`, which tells us that this slice\nhave 2 elements in it.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst sl = ns[1..3];\ntry stdout.print(\"{d}\\n\", .{sl.len});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n2\n```\n\n\n:::\n:::\n\n\n\n\n\n### Array operators\n\nThere are two array operators available in Zig that are very useful.\nThe array concatenation operator (`++`), and the array multiplication operator (`**`). As the name suggests,\nthese are array operators.\n\nOne important detail about these two operators is that they work\nonly when both operands have a size (or \"length\") that is compile-time known.\nWe are going to talk more about\nthe differences between \"compile-time known\" and \"runtime known\" at @sec-compile-time.\nBut for now, keep this information in mind, that you cannot use these operators in every situation.\n\nIn summary, the `++` operator creates a new array that is the concatenation,\nof both arrays provided as operands. So, the expression `a ++ b` produces\na new array which contains all the elements from arrays `a` and `b`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst a = [_]u8{1,2,3};\nconst b = [_]u8{4,5};\nconst c = a ++ b;\ntry stdout.print(\"{any}\\n\", .{c});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n{ 1, 2, 3, 4, 5 }\n```\n\n\n:::\n:::\n\n\n\n\nThis `++` operator is particularly useful to concatenate strings together.\nStrings in Zig are described in depth at @sec-zig-strings. In summary, a string object in Zig\nis essentially an arrays of bytes. So, you can use this array concatenation operator\nto effectively concatenate strings together.\n\nIn contrast, the `**` operator is used to replicate an array multiple\ntimes. In other words, the expression `a ** 3` creates a new array\nwhich contains the elements of the array `a` repeated 3 times.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst a = [_]u8{1,2,3};\nconst c = a ** 2;\ntry stdout.print(\"{any}\\n\", .{c});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n{ 1, 2, 3, 1, 2, 3 }\n```\n\n\n:::\n:::\n\n\n\n\n\n## Blocks and scopes {#sec-blocks}\n\nBlocks are created in Zig by a pair of curly braces. A block is just a group of\nexpressions (or statements) contained inside of a pair of curly braces. All of these expressions that\nare contained inside of this pair of curly braces belongs to the same scope.\n\nIn other words, a block just delimits a scope in your code.\nThe objects that you define inside the same block belongs to the same\nscope, and, therefore, are accessible from within this scope.\nAt the same time, these objects are not accessible outside of this scope.\nSo, you could also say that blocks are used to limit the scope of the objects that you create in\nyour source code. In less technical terms, blocks are used to specify where in your source code\nyou can access whatever object you have in your source code.\n\nSo, a block is just a group of expressions contained inside a pair of curly braces.\nAnd every block have it's own scope separated from the others.\nThe body of a function is a classic example of a block. If statements, for and while loops\n(and any other structure in the language that uses the pair of curly braces)\nare also examples of blocks.\n\nThis means that, every if statement, or for loop,\netc., that you create in your source code have it's own separate scope.\nThat is why you can't access the objects that you defined inside\nof your for loop (or if statement) in an outer scope, i.e. a scope outside of the for loop.\nBecause you are trying to access an object that belongs to a scope that is different\nthan your current scope.\n\n\nYou can create blocks within blocks, with multiple levels of nesting.\nYou can also (if you want to) give a label to a particular block, with the colon character (`:`).\nJust write `label:` before you open the pair of curly braces that delimits your block. When you label a block\nin Zig, you can use the `break` keyword to return a value from this block, like as if it\nwas a function's body. You just write the `break` keyword, followed by the block label in the format `:label`,\nand the expression that defines the value that you want to return.\n\nLike in the example below, where we are returning the value from the `y` object\nfrom the block `add_one`, and saving the result inside the `x` object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar y: i32 = 123;\nconst x = add_one: {\n y += 1;\n break :add_one y;\n};\nif (x == 124 and y == 124) {\n try stdout.print(\"Hey!\", .{});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHey!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n\n## How strings work in Zig? {#sec-zig-strings}\n\nThe first project that we are going to build and discuss in this book is a base64 encoder/decoder (@sec-base64).\nBut in order for us to build such a thing, we need to get a better understanding on how strings work in Zig.\nSo let's discuss this specific aspect of Zig.\n\nIn Zig, a string literal value is just a pointer to a null-terminated array of bytes (i.e. the same thing as a C string).\nHowever, a string object in Zig is a little more than just a pointer. A string object\nin Zig is an object of type `[]const u8`, and, this object always contains two things: the\nsame null-terminated array of bytes that you would find in a string literal value, plus a length value.\nEach byte in this \"array of bytes\" is represented by an `u8` value, which is an unsigned 8 bit integer,\nso, it is equivalent to the C data type `unsigned char`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// This is a string literal value:\n\"A literal value\";\n// This is a string object:\nconst object: []const u8 = \"A string object\";\n```\n:::\n\n\n\n\nZig always assumes that this sequence of bytes is UTF-8 encoded. This might not be true for every\nsequence of bytes you have it, but is not really Zig's job to fix the encoding of your strings\n(you can use [`iconv`](https://www.gnu.org/software/libiconv/)[^libiconv] for that).\nToday, most of the text in our modern world, specially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely\nhave problems in Zig.\n\n[^libiconv]: \n\nLet’s take for example the word \"Hello\". In UTF-8, this sequence of characters (H, e, l, l, o)\nis represented by the sequence of decimal numbers 72, 101, 108, 108, 111. In xecadecimal, this\nsequence is `0x48`, `0x65`, `0x6C`, `0x6C`, `0x6F`. So if I take this sequence of hexadecimal values,\nand ask Zig to print this sequence of bytes as a sequence of characters (i.e. a string), then,\nthe text \"Hello\" will be printed into the terminal:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n const bytes = [_]u8{0x48, 0x65, 0x6C, 0x6C, 0x6F};\n try stdout.print(\"{s}\\n\", .{bytes});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHello\n```\n\n\n:::\n:::\n\n\n\n\n\nIf you want to see the actual bytes that represents a string in Zig, you can use\na `for` loop to iterate through each byte in the string, and ask Zig to print each byte as an hexadecimal\nvalue to the terminal. You do that by using a `print()` statement with the `X` formatting specifier,\nlike you would normally do with the [`printf()` function](https://cplusplus.com/reference/cstdio/printf/)[^printfs] in C.\n\n[^printfs]: \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example of string literal in Zig\";\n try stdout.print(\"Bytes that represents the string object: \", .{});\n for (string_object) |byte| {\n try stdout.print(\"{X} \", .{byte});\n }\n try stdout.print(\"\\n\", .{});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nBytes that represents the string object: 54 68 69 \n 73 20 69 73 20 61 6E 20 65 78 61 6D 70 6C 65 20 6F\n F 66 20 73 74 72 69 6E 67 20 6C 69 74 65 72 61 6C 2\n 20 69 6E 20 5A 69 67 \n```\n\n\n:::\n:::\n\n\n\n\n### Strings in C\n\nAt first glance, this looks very similar to how C treats strings as well. In more details, string values\nin C are treated internally as an array of arbitrary bytes, and this array is also null-terminated.\n\nBut one key difference between a Zig string and a C string, is that Zig also stores the length of\nthe array inside the string object. This small detail makes your code safer, because is much\neasier for the Zig compiler to check if you are trying to access an element that is \"out of bounds\", i.e. if\nyour trying to access memory that does not belong to you.\n\nTo achieve this same kind of safety in C, you have to do a lot of work that kind of seems pointless.\nSo getting this kind of safety is not automatic and much harder to do in C. For example, if you want\nto track the length of your string troughout your program in C, then, you first need to loop through\nthe array of bytes that represents this string, and find the null element (`'\\0'`) position to discover\nwhere exactly the array ends, or, in other words, to find how much elements the array of bytes contain.\n\nTo do that, you would need something like this in C. In this example, the C string stored in\nthe object `array` is 25 bytes long:\n\n```c\n#include \nint main() {\n char* array = \"An example of string in C\";\n int index = 0;\n while (1) {\n if (array[index] == '\\0') {\n break;\n }\n index++;\n }\n printf(\"Number of elements in the array: %d\\n\", index);\n}\n```\n\n```\nNumber of elements in the array: 25\n```\n\nBut in Zig, you do not have to do this, because the object already contains a `len`\nfield which stores the length information of the array. As an example, the `string_object` object below is 43 bytes long:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example of string literal in Zig\";\n try stdout.print(\"{d}\\n\", .{string_object.len});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n43\n```\n\n\n:::\n:::\n\n\n\n\n\n### A better look at the object type\n\nNow, we can inspect better the type of objects that Zig create. To check the type of any object in Zig, you can use the\n`@TypeOf()` function. If we look at the type of the `simple_array` object below, you will find that this object\nis a array of 4 elements. Each element is a signed integer of 32 bits which corresponds to the data type `i32` in Zig.\nThat is what an object of type `[4]i32` is.\n\nBut if we look closely at the type of the `string_object` object below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 43 elements (or 43 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[43:0]u8` portion of the type below.\nIn other words, the string stored inside the `string_object` object is 43 bytes long.\nThat is why you have the type `*const [43:0]u8` below.\n\nIn the case of `string_object`, it is a constant pointer (`*const`) because the object `string_object` is declared\nas constant in the source code (in the line `const string_object = ...`). So, if we changed that for some reason, if\nwe declare `string_object` as a variable object (i.e. `var string_object = ...`), then, `string_object` would be\njust a normal pointer to an array of unsigned 8-bit integers (i.e. `* [43:0]u8`).\n\nNow, if we create an pointer to the `simple_array` object, then, we get a constant pointer to an array of 4 elements (`*const [4]i32`),\nwhich is very similar to the type of the `string_object` object. This demonstrates that a string object (or a string literal)\nin Zig is already a pointer to an array.\n\nJust remember that a \"pointer to an array\" is different than an \"array\". So a string object in Zig is a pointer to an array\nof bytes, and not simply an array of bytes.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example of string literal in Zig\";\n const simple_array = [_]i32{1, 2, 3, 4};\n try stdout.print(\"Type of array object: {}\", .{@TypeOf(simple_array)});\n try stdout.print(\n \"Type of string object: {}\",\n .{@TypeOf(string_object)}\n );\n try stdout.print(\n \"Type of a pointer that points to the array object: {}\",\n .{@TypeOf(&simple_array)}\n );\n}\n```\n:::\n\n\n\n\n```\nType of array object: [4]i32\nType of string object: *const [43:0]u8\nType of a pointer that points to\n the array object: *const [4]i32\n```\n\n\n### Byte vs unicode points\n\nIs important to point out that each byte in the array is not necessarily a single character.\nThis fact arises from the difference between a single byte and a single unicode point.\n\nThe encoding UTF-8 works by assigning a number (which is called a unicode point) to each character in\nthe string. For example, the character \"H\" is stored in UTF-8 as the decimal number 72. This means that\nthe number 72 is the unicode point for the character \"H\". Each possible character that can appear in a\nUTF-8 encoded string have its own unicode point.\n\nFor example, the Latin Capital Letter A With Stroke (Ⱥ) is represented by the number (or the unicode point)\n570. However, this decimal number (570) is higher than the maximum number stored inside a single byte, which\nis 255. In other words, the maximum decimal number that can be represented with a single byte is 255. That is why,\nthe unicode point 570 is actually stored inside the computer’s memory as the bytes `C8 BA`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"Ⱥ\";\n try stdout.print(\"Bytes that represents the string object: \", .{});\n for (string_object) |char| {\n try stdout.print(\"{X} \", .{char});\n }\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nBytes that represents the string object: C8 BA \n```\n\n\n:::\n:::\n\n\n\n\n\nThis means that to store the character Ⱥ in an UTF-8 encoded string, we need to use two bytes together\nto represent the number 570. That is why the relationship between bytes and unicode points is not always\n1 to 1. Each unicode point is a single character in the string, but not always a single byte corresponds\nto a single unicode point.\n\nAll of this means that if you loop trough the elements of a string in Zig, you will be looping through the\nbytes that represents that string, and not through the characters of that string. In the Ⱥ example above,\nthe for loop needed two iterations (instead of a single iteration) to print the two bytes that represents this Ⱥ letter.\n\nNow, all english letters (or ASCII letters if you prefer) can be represented by a single byte in UTF-8. As a\nconsequence, if your UTF-8 string contains only english letters (or ASCII letters), then, you are lucky. Because\nthe number of bytes will be equal to the number of characters in that string. In other words, in this specific\nsituation, the relationship between bytes and unicode points is 1 to 1.\n\nBut on the other side, if your string contains other types of letters… for example, you might be working with\ntext data that contains, chinese, japanese or latin letters, then, the number of bytes necessary to represent\nyour UTF-8 string will likely be much higher than the number of characters in that string.\n\nIf you need to iterate through the characters of a string, instead of its bytes, then, you can use the\n`std.unicode.Utf8View` struct to create an iterator that iterates through the unicode points of your string.\n\nIn the example below, we loop through the japanese characters “アメリカ”. Each of the four characters in\nthis string is represented by three bytes. But the for loop iterates four times, one iteration for each\ncharacter/unicode point in this string:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n var utf8 = (\n (try std.unicode.Utf8View.init(\"アメリカ\"))\n .iterator()\n );\n while (utf8.nextCodepointSlice()) |codepoint| {\n try stdout.print(\n \"got codepoint {}\\n\",\n .{std.fmt.fmtSliceHexUpper(codepoint)}\n );\n }\n}\n```\n:::\n\n\n\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecome more interconnected with techology and computers,\nthe data produced by all of this technology becomes one of the most important\n(and also, one of the most dangerous) assets that we have.\n\nThis is probably the main reason why modern low-level programming languages\nhave been giving great attention to safety, specially memory safety, because\nmemory corruption is still the main target for hackers to exploit.\nThe reality is that we don't have an easy solution for this problem.\nFor now, we only have techniques and strategies that mitigates these\nproblems.\n\nAs Richard Feldman explains on his [most recent GOTO conference talk](https://www.youtube.com/watch?v=jIZpKpLCOiU&ab_channel=GOTOConferences)[^gotop]\n, we haven't figured it out yet a way to achieve **true safety in technology**.\nIn other words, we haven't found a way to build software that won't be exploited\nwith 100% certainty. We can greatly reduce the risks of our software being\nexploited, by ensuring memory safety for example. But this is not enough\nto achieve \"true safety\" territory.\n\nBecause even if you write your program in a \"safe language\", hackers can still\nexploit failures in the operational system where your program is running (e.g. maybe the\nsystem where your code is running have a \"backdoor exploit\" that can still\naffect your code in unexpected ways), or also, they can exploit the features\nfrom the architecture of your computer. A recently found exploit\nthat involves memory invalidation through a feature of \"memory tags\"\npresent in ARM chips is an example of that [@exploit1].\n\n[^gotop]: \n\nThe question is: what Zig and other languages have been doing to mitigate this problem?\nIf we take Rust as an example, Rust is, for the most part[^rust-safe], a memory safe\nlanguage by enforcing specific rules to the developer. In other words, the key feature\nof Rust, the *borrow checker*, forces you to follow a specific logic when you are writing\nyour Rust code, and the Rust compiler will always complain everytime you try to go out of this\npattern.\n\n[^rust-safe]: Actually, a lot of existing Rust code is still memory unsafe, because they communicate with external libraries through FFI (*foreign function interface*), which disables the borrow-checker features through the `unsafe` keyword.\n\n\nIn contrast, the Zig language is not a memory safe language by default.\nInstead of forcing the developer to follow a specific rule, the Zig language\nachieves memory safety by offering tools that the developer can use for this purpose.\nIn other words, the `zig` compiler does not obligates you to use such tools.\nBut there is often no reason to not use these tools in your Zig code,\nso you often achieve a similar level of memory safety of Rust in Zig\nby simply using these tools.\n\nThe tools listed below are related to memory safety in Zig. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations phisically close to allocations. This helps you to avoid memory leaks, \"use after free\", and also \"double-free\" problems. Furthermore, it also keeps free operations logically tied to the end of the current scope, which greatly reduces the mental overhead about object lifetime.\n- `errdefer` helps you to garantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and object are non-nullable by default. This helps you to avoid memory problems that might arise from de-referencing null pointers.\n- Zig offers some native types of allocators (called \"testing allocators\") that can detect memory leaks and double-frees. These types of allocators are widely used on unit tests, so they make your unit tests a weapon that you can use to detect memory problems in your code.\n- arrays and slices in Zig have their lengths embedded in the object itself, which makes the `zig` compiler very effective on detecting \"index out-of-range\" type of errors, and avoiding buffer overflows.\n\n\nDespite these features that Zig offers that are related to memory safety issues, the language\nalso have some rules that help you to achieve another type of safety, which is more related to\nprogram logic safety. These rules are:\n\n- pointers and objects are non-nullable by default. Which eliminates an edge case that might break the logic of your program.\n- switch statements must exaust all possible options.\n- the `zig` compiler forces you to handle every possible error.\n\n\n## Other parts of Zig\n\nWe already learned a lot about Zig's syntax, and also, some pretty technical\ndetails about it. Just as a quick recap:\n\n- We talked about how functions are written in Zig at @sec-root-file and @sec-main-file.\n- How to create new objects/identifiers at @sec-root-file and specially at @sec-assignments.\n- How strings work in Zig at @sec-zig-strings.\n- How to use arrays and slices at @sec-arrays.\n- How to import functionality from other Zig modules at @sec-root-file.\n\n\nBut, for now, this amount of knowledge is enough for us to continue with this book.\nLater, over the next chapters we will still talk more about other parts of\nZig's syntax that are also equally important as the other parts. Such as:\n\n\n- How Object-Oriented programming can be done in Zig through *struct declarations* at @sec-structs-and-oop.\n- Basic control flow syntax at @sec-zig-control-flow.\n- Enums at @sec-enum;\n- Pointers and Optionals at @sec-pointer;\n- Error handling with `try` and `catch` at @sec-error-handling;\n- Unit tests at @sec-unittests;\n- Vectors;\n- Build System at @sec-build-system;\n\n\n\n\n", + "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n\n# Introducing Zig\n\nIn this chapter, I want to introduce you to the world of Zig.\nZig is a very young language that is being actively developed.\nAs a consequence, it's world is still very wild and to be explored.\nThis book is my attempt to help you on your personal journey for\nunderstanding and exploring the exciting world of Zig.\n\nI assume you have previous experience with some programming\nlanguage in this book, not necessarily with a low-level one.\nSo, if you have experience with Python, or Javascript, for example, is fine.\nBut, if you do have experience with low-level languages, such as C, C++, or\nRust, you will probably learn faster throughout this book.\n\n## What is Zig?\n\nZig is a modern, low-level, and general-purpose programming language. Some programmers think of\nZig as a modern and better version of C.\n\nIn the author's personal interpretation, Zig is tightly connected with \"less is more\".\nInstead of trying to become a modern language by adding more and more features,\nmany of the core improvements that Zig brings to the\ntable are actually about removing annoying and evil behaviours/features from C and C++.\nIn other words, Zig tries to be better by simplifying the language, and by having more consistent and robust behaviour.\nAs a result, analyzing, writing and debugging applications become much easier and simpler in Zig, than it is in C or C++.\n\nThis philosophy becomes clear with the following phrase from the official website of Zig:\n\n> \"Focus on debugging your application rather than debugging your programming language knowledge\".\n\nThis phrase is specially true for C++ programmers. Because C++ is a gigantic language,\nwith tons of features, and also, there are lots of different \"flavors of C++\". These elements\nare what makes C++ so much complex and hard to learn. Zig tries to go in the opposite direction.\nZig is a very simple language, more closely related to other simple languages such as C and Go.\n\nThe phrase above is still important for C programmers too. Because, even C being a simple\nlanguage, it is still hard sometimes to read and understand C code. For example, pre-processor macros in\nC are an evil source of confusion. They really makes it hard sometimes to debug\nC programs. Because macros are essentially a second language embedded in C that obscures\nyour C code. With macros, you are no longer 100% sure about which pieces\nof code are being sent to the compiler. It obscures the actual source code that you wrote.\n\nYou don't have macros in Zig. In Zig, the code you write, is the actual code that get's compiled by the compiler.\nYou don't have evil features that obscures you code.\nYou also don't have hidden control flow happening behind the scenes. And, you also\ndon't have functions or operators from the standard library that make\nhidden memory allocations behind your back.\n\nBy being a simpler language, Zig becomes much more clear and easier to read/write,\nbut at the same time, it also achieves a much more robust state, with more consistent\nbehaviour in edge situations. Once again, less is more.\n\n\n## Hello world in Zig\n\nWe begin our journey in Zig by creating a small \"Hello World\" program.\nTo start a new Zig project in your computer, you simply call the `init` command\nfrom the `zig` compiler.\nJust create a new directory in your computer, then, init a new Zig project\ninside this directory, like this:\n\n```bash\nmkdir hello_world\ncd hello_world\nzig init\n```\n\n```\ninfo: created build.zig\ninfo: created build.zig.zon\ninfo: created src/main.zig\ninfo: created src/root.zig\ninfo: see `zig build --help` for a menu of options\n```\n\n### Understanding the project files {#sec-project-files}\n\nAfter you run the `init` command from the `zig` compiler, some new files\nare created inside of your current directory. First, a \"source\" (`src`) directory\nis created, containing two files, `main.zig` and `root.zig`. Each `.zig` file\nis a separate Zig module, which is simply a text file that contains some Zig code.\n\nBy convention, the `main.zig` module is where your main function lives. Thus,\nif you are building an executable program in Zig, you need to declare a `main()` function,\nwhich represents the entrypoint of your program, i.e. it is where the execution of your program begins.\n\nHowever, if you are building a library (instead of an executable program), then,\nthe normal procedure is to delete this `main.zig` file and start with the `root.zig` module.\nBy convention, the `root.zig` module is the root source file of your library.\n\n```bash\ntree .\n```\n\n```\n.\n├── build.zig\n├── build.zig.zon\n└── src\n ├── main.zig\n └── root.zig\n\n1 directory, 4 files\n```\n\nThe `ìnit` command also creates two additional files in our working directory:\n`build.zig` and `build.zig.zon`. The first file (`build.zig`) represents a build script written in Zig.\nThis script is executed when you call the `build` command from the `zig` compiler.\nIn other words, this file contain Zig code that executes the necessary steps to build the entire project.\n\nIn general, low-level languages normally use a compiler to build your\nsource code into binary executables or binary libraries.\nNevertheless, this process of compiling your source code and building\nbinary executables or binary libraries from it, became a real challenge\nin the programming world, once the projects became bigger and bigger.\nAs a result, programmers created \"build systems\", which are a second set of tools designed to make this process\nof compiling and building complex projects, easier.\n\nExamples of build systems are CMake, GNU Make, GNU Autoconf and Ninja,\nwhich are used to build complex C and C++ projects.\nWith these systems, you can write scripts, which are called \"build scripts\".\nThey simply are scripts that describes the necessary steps to compile/build\nyour project.\n\nHowever, these are separate tools, that do not\nbelong to C/C++ compilers, like `gcc` or `clang`.\nAs a result, in C/C++ projects, you have not only to install and\nmanage your C/C++ compilers, but you also have to install and manage\nthese build systems separately.\n\nBut instead of using a separate build system, in Zig, we use the\nZig language itself to write build scripts.\nIn other words, Zig contains a native build system in it. And\nwe can use this build system to write small scripts in Zig,\nwhich describes the necessary steps to build/compile our Zig project[^zig-build-system].\nSo, everything you need to build a complex Zig project is the\n`zig` compiler, and nothing more.\n\n[^zig-build-system]: .\n\n\nThe second generated file (`build.zig.zon`) is the Zig package manager configuration file,\nwhere you can list and manage the dependencies of your project. Yes, Zig has\na package manager (like `pip` in Python, `cargo` in Rust, or `npm` in Javascript) called Zon,\nand this `build.zig.zon` file is similar to the `package.json` file\nin Javascript projects, or, the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects.\n\n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have notice that every line of code with an expression ends with a semicolon (`;`).\nThis follows the syntax of a C-family programming language[^c-family].\n\n[^c-family]: \n\nAlso, notice the `@import()` call at the first line. We use this built-in function\nto import functionality from other Zig modules into our current module.\nIn other words, this `@import()` function works similarly to the `#include` pre-processor\nin C or C++, or, to the `import` statement in Python or Javascript code.\nIn this example, we are importing the `std` module,\nwhich gives you access to the Zig Standard Library.\n\nIn this `root.zig` file, we can also see how assignments (i.e. creating new objects)\nare made in Zig. You can create a new object in Zig by using the following syntax\n`(const|var) name = value;`. In the example below, we are creating two constant\nobjects (`std` and `testing`). At @sec-assignments we talk more about objects in general.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst testing = std.testing;\n\nexport fn add(a: i32, b: i32) i32 {\n return a + b;\n}\n```\n:::\n\n\n\n\nFunctions in Zig are declared using the `fn` keyword.\nIn this `root.zig` module, we are declaring a function called `add()`, which has two arguments named `a` and `b`.\nThe function returns an integer of the type `i32` as result.\n\n\nZig is not exactly a strongly-typed language. Because you can (if you want to) omit\nthe type of an object in your code, if this type can be derived from the assigned value.\nBut there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of every single function argument, and also,\nthe return type of every function you create in Zig. So, at least in function declarations,\nZig is a strongly-typed language.\n\nWe specify the type of an object or a function argument in Zig, by\nusing a colon character (`:`) followed by the type after the name of this object/function argument.\nWith the expressions `a: i32` and `b: i32`, we know that, both `a` and `b` arguments have type `i32`,\nwhich is a signed 32 bit integer. In this part,\nthe syntax in Zig is identical to the syntax in Rust, which also specifies types by\nusing the colon character.\n\nLastly, we have the return type of the function at the end of the line, before we open\nthe curly braces to start writing the function's body. In the example above, this type is also\na signed 32 bit integer (`i32`) value.\n\nNotice that we also have an `export` keyword before the function declaration. This keyword\nis similar to the `extern` keyword in C. It exposes the function\nto make it available in the library API.\n\n\nIn other words, if you have a project where you are currently building\na library for other people to use, you need to expose your functions\nso that they are available in the library's API, so that users can use it.\nIf we removed the `export` keyword from the `add()` function declaration,\nthen, this function would be no longer exposed in the library object built\nby the `zig` compiler.\n\n\nHaving that in mind, the keyword `export` is a keyword used in libraries written in Zig.\nIf you are not currently writing a library in your project, then, you do not need to\ncare about this keyword.\n\n\n### The `main.zig` file {#sec-main-file}\n\nNow that we have learned a lot about Zig's syntax from the `root.zig` file,\nlet's take a look at the `main.zig` file.\nA lot of the elements we saw in `root.zig` are also present in `main.zig`.\nBut we have some other elements that we did not have seen yet, so let's dive in.\n\nFirst, look at the return type of the `main()` function in this file.\nWe can see a small change. Now, the return\ntype of the function (`void`) is accompanied by an exclamation mark (`!`).\nWhat this exclamation mark is telling us, is that this `main()` function\nmight also return an error.\n\nSo, in this example, the `main()` function can either return `void`, or, return an error.\nThis is an interesting feature of Zig. If you write a function, and, something inside of\nthe body of this function might return an error, then, you are forced to:\n\n- either add the exclamation mark to the return type of the function, to make it clear that\nthis function might return an error.\n- or explicitly handle this error that might occur inside the function, to make sure that,\nif this error does happen, you are prepared, and your function will no longer return an error\nbecause you handled the error inside your function.\n\nIn most programming languages, we normally handle (or deals with) an error through\na *try catch* pattern, and Zig, this is no different. But, if we look at the `main()` function\nbelow, you can see that we do have a `try` keyword in the 5th line. But we do not have a `catch` keyword\nin this code.\n\nThis means that, we are using the keyword `try` to execute a code that might return an error,\nwhich is the `stdout.print()` expression. But because we do not have a `catch` keyword in this line,\nwe are not treating (or dealing with) this error.\nSo, if this expression do return an error, we are not catching and solving this error in any way.\nThat is why the exclamation mark was added to the return type of the function.\n\nSo, in essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword do nothing essentially. It simply passes this value forward. But, if the expression do\nreturn an error, then, the `try` keyword will unwrap and return this error from the function, and also print it's\nstack trace to `stderr`.\n\nThis might sound weird to you, if you come from a high-level language. Because in\nhigh-level languages, such as Python, if an error occurs somewhere, this error is automatically\nreturned and the execution of your program will automatically stops, even if you don't want\nto stop the execution. You are obligated to face the error.\n\nBut if you come from a low-level language, then, maybe, this idea do not sound so weird or distant to you.\nBecause in C for example, normally functions doesn't raise errors, or, they normally don't stop the execution.\nIn C, error handling\nis done by constantly checking the return value of the function. So, you run the function,\nand then, you use an if statement to check if the function returned a value that is valid,\nor, if it returned an error. If an error was returned from the function, then, the if statement\nwill execute some code that fixes this error.\n\nSo, at least for C programmers, they do need to write a lot of if statements to\nconstantly check for errors around their code. And because of that, this simple feature from Zig, might be\nextraordinary for them. Because this `try` keyword can automatically unwrap the error,\nand warn you about this error, and let you deal with it, without any extra work from the programmer.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n```\n:::\n\n\n\n\nNow, another thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. This keyword means\n\"public\". It marks the `main()` function as a *public function* from this module.\n\nIn other words, every function that you declare in your Zig module is, by default, a private (or \"static\")\nfunction that belongs to this Zig module, and can only be used (or called) from within this same module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\nThis means that the `pub` keyword in Zig do essentially the opposite of what the `static` keyword\ndo in C/C++.\n\nBy making a function \"public\", you allow other Zig modules to access and call this function,\nand use it for they own purposes.\nall these other Zig modules need to do is, to import your module with the `@import()`\nbuilt-in function. Then, they get access to all public functions that are present in\nyour Zig module.\n\n\n### Compiling your source code {#sec-compile-code}\n\nYou can compile your Zig modules into a binary executable by running the `build-exe` command\nfrom the `zig` compiler. You simply list all the Zig modules that you want to build after\nthe `build-exe` command, separated by spaces. In the example below, we are compiling the module `main.zig`.\n\n```bash\nzig build-exe src/main.zig\n```\n\nSince we are building an executable, the `zig` compiler will look for a `main()` function\ndeclared in any of the files that you list after the `build-exe` command. If\nthe compiler does not find a `main()` function declared somewhere, a\ncompilation error will be raised, warning about this mistake.\n\nThe `zig` compiler also offers a `build-lib` and `build-obj` commands, which work\nthe exact same way as the `build-exe` command. The only difference is that, they compile your\nZig modules into a portale C ABI library, or, into object files, respectively.\n\nIn the case of the `build-exe` command, a binary executable file is created by the `zig`\ncompiler in the root directory of your project.\nIf we take a look now at the contents of our current directory, with a simple `ls` command, we can\nsee the binary file called `main` that was created by the compiler.\n\n```bash\nls\n```\n\n```\nbuild.zig build.zig.zon main src\n```\n\nIf I execute this binary executable, I get the \"Hello World\" message in the terminal\n, as we expected.\n\n```bash\n./main\n```\n\n```\nHello, world!\n```\n\n\n### Compile and execute at the same time {#sec-compile-run-code}\n\nOn the previous section, I presented the `zig build-exe` command, which\ncompiles Zig modules into an executable file. However, this means that,\nin order to execute the executable file, we have to run two different commands.\nFirst, the `zig build-exe` command, and then, we call the executable file\ncreated by the compiler.\n\nBut what if we wanted to perform these two steps,\nall at once, in a single command? We can do that by using the `zig run`\ncommand.\n\n```bash\nzig run src/main.zig\n```\n\n```\nHello, world!\n```\n\n### Compiling the entire project {#sec-compile-project}\n\nJust as I described at @sec-project-files, as our project grows in size and\ncomplexity, we usually prefer to organize the compilation and build process\nof the project into a build script, using some sort of \"build system\".\n\nIn other words, as our project grows in size and complexity,\nthe `build-exe`, `build-lib` and `build-obj` commands become\nharder to use directly. Because then, we start to list\nmultiple and multiple modules at the same time. We also\nstart to add built-in compilation flags to customize the\nbuild process for our needs, etc. It becomes a lot of work\nto write the necessary commands by hand.\n\nIn C/C++ projects, programmers normally opt to use CMake, Ninja, `Makefile` or `configure` scripts\nto organize this process. However, in Zig, we have a native build system in the language itself.\nSo, we can write build scripts in Zig to compile and build Zig projects. Then, all we\nneed to do, is to call the `zig build` command to build our project.\n\nSo, when you execute the `zig build` command, the `zig` compiler will search\nfor a Zig module named `build.zig` inside your current directory, which\nshould be your build script, containing the necessary code to compile and\nbuild your project. If the compiler do find this `build.zig` file in your directory,\nthen, the compiler will essentially execute a `zig run` command\nover this `build.zig` file, to compile and execute this build\nscript, which in turn, will compile and build your entire project.\n\n\n```bash\nzig build\n```\n\n\nAfter you execute this \"build project\" command, a `zig-out` directory\nis created in the root of your project directory, where you can find\nthe binary executables and libraries created from your Zig modules\naccordingly to the build commands that you specified at `build.zig`.\nWe will talk more about the build system in Zig latter in this book.\n\nIn the example below, I'm executing the binary executable\nnamed `hello_world` that was generated by the compiler after the\n`zig build` command.\n\n```bash\n./zig-out/bin/hello_world\n```\n\n```\nHello, world!\n```\n\n\n\n## How to learn Zig?\n\nWhat are the best strategies to learn Zig? \nFirst of all, of course this book will help you a lot on your journey through Zig.\nBut you will also need some extra resources if you want to be really good at Zig.\n\nAs a first tip, you can join a community with Zig programmers to get some help\n, when you need it:\n\n- Reddit forum: ;\n- Ziggit community: ;\n- Discord, Slack, Telegram, and others: ;\n\nNow, one of the best ways to learn Zig is to simply read Zig code. Try\nto read Zig code often, and things will become more clear.\nA C/C++ programmer would also probably give you this same tip.\nBecause this strategy really works!\n\nNow, where you can find Zig code to read?\nI personally think that, the best way of reading Zig code is to read the source code of the\nZig Standard Library. The Zig Standard Library is available at the [`lib/std` folder](https://github.com/ziglang/zig/tree/master/lib/std)[^zig-lib-std] on\nthe official GitHub repository of Zig. Access this folder, and start exploring the Zig modules.\n\nAlso, a great alternative is to read code from other large Zig\ncodebases, such as:\n\n1. the [Javascript runtime Bun](https://github.com/oven-sh/bun)[^bunjs].\n1. the [game engine Mach](https://github.com/hexops/mach)[^mach].\n1. a [LLama 2 LLM model implementation in Zig](https://github.com/cgbur/llama2.zig/tree/main)[^ll2].\n1. the [financial transactions database `tigerbeetle`](https://github.com/tigerbeetle/tigerbeetle)[^tiger].\n1. the [command-line arguments parser `zig-clap`](https://github.com/Hejsil/zig-clap)[^clap].\n1. the [UI framework `capy`](https://github.com/capy-ui/capy)[^capy].\n1. the [Language Protocol implementation for Zig, `zls`](https://github.com/zigtools/zls)[^zls].\n1. the [event-loop library `libxev`](https://github.com/mitchellh/libxev)[^xev].\n\n[^xev]: \n[^zls]: \n[^capy]: \n[^clap]: \n[^tiger]: \n[^ll2]: \n[^mach]: \n[^bunjs]: .\n\nAll these assets are available on GitHub,\nand this is great, because we can use the GitHub search bar in our advantage,\nto find Zig code that fits our description.\nFor example, you can always include `lang:Zig` in the GitHub search bar when you\nare searching for a particular pattern. This will limit the search to only Zig modules.\n\n[^zig-lib-std]: \n\nAlso, a great alternative is to consult online resources and documentations.\nHere is a quick list of resources that I personally use from time to time to learn\nmore about the language each day:\n\n- Zig Language Reference: ;\n- Zig Standard Library Reference: ;\n- Zig Guide: ;\n- Karl Seguin Blog: ;\n- Zig News: ;\n- Read the code written by one of the Zig core team members: ;\n- Some livecoding sessions are transmitted in the Zig Showtime Youtube Channel: ;\n\n\nAnother great strategy to learn Zig, or honestly, to learn any language you want,\nis to practice it by solving exercises. For example, there is a famous repository\nin the Zig community called [Ziglings](https://codeberg.org/ziglings/exercises/)[^ziglings]\n, which contains more than 100 small exercises that you can solve. It is a repository of\ntiny programs written in Zig that are currently broken, and your responsibility is to\nfix these programs, and make them work again.\n\n[^ziglings]: .\n\nA famous tech YouTuber known as *The Primeagen* also posted some videos (at YouTube)\nwhere he solves these exercises from Ziglings. The first video is named\n[\"Trying Zig Part 1\"](https://www.youtube.com/watch?v=OPuztQfM3Fg&t=2524s&ab_channel=TheVimeagen)[^prime1].\n\n[^prime1]: .\n\nAnother great alternative, is to solve the [Advent of Code exercises](https://adventofcode.com/)[^advent-code].\nThere are people that already took the time to learn and solve the exercises, and they posted\ntheir solutions on GitHub as well, so, in case you need some resource to compare while solving\nthe exercises, you can look at these two repositories:\n\n- ;\n- ;\n\n[^advent-code]: \n\n\n\n\n\n\n## Creating new objects in Zig (i.e. identifiers) {#sec-assignments}\n\nLet's talk more about objects in Zig. Readers that have past experience\nwith other programming languages might know this concept through\na different name, such as: \"variable\" or \"identifier\". In this book, I choose\nto use the term \"object\" to refer to this concept.\n\nTo create a new object (or a new \"identifier\") in Zig, we use\nthe keywords `const` or `var`. These keywords specificy if the object\nthat you are creating is mutable or not.\nIf you use `const`, then the object you are\ncreating is a constant (or immutable) object, which means that once you declare this object, you\ncan no longer change the value stored inside this object.\n\nOn the other side, if you use `var`, then, you are creating a variable (or mutable) object.\nYou can change the value of this object as many times you want. Using the\nkeyword `var` in Zig is similar to using the keywords `let mut` in Rust.\n\n### Constant objects vs variable objects\n\nIn the code example below, we are creating a new constant object called `age`.\nThis object stores a number representing the age of someone. However, this code example\ndoes not compiles succesfully. Because on the next line of code, we are trying to change the value\nof the object `age` to 25.\n\nThe `zig` compiler detects that we are trying to change\nthe value of an object/identifier that is constant, and because of that,\nthe compiler will raise a compilation error, warning us about the mistake.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 24;\n// The line below is not valid!\nage = 25;\n```\n:::\n\n\n\n\n```\nt.zig:10:5: error: cannot assign to constant\n age = 25;\n ~~^~~\n```\n\nIn contrast, if you use `var`, then, the object created is a variable object.\nWith `var` you can declare this object in your source code, and then,\nchange the value of this object how many times you want over future points\nin your source code.\n\nSo, using the same code example exposed above, if I change the declaration of the\n`age` object to use the `var` keyword, then, the program gets compiled succesfully.\nBecause now, the `zig` compiler detects that we are changing the value of an\nobject that allows this behaviour, because it is an \"variable object\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\n:::\n\n\n\n\n\n### Declaring without an initial value\n\nBy default, when you declare a new object in Zig, you must give it\nan initial value. In other words, this means\nthat we have to declare, and, at the same time, initialize every object we\ncreate in our source code.\n\nOn the other hand, you can, in fact, declare a new object in your source code,\nand not give it an explicit value. But we need to use a special keyword for that,\nwhich is the `undefined` keyword.\n\nIs important to emphasize that, you should avoid using `undefined` as much as possible.\nBecause when you use this keyword, you leave your object uninitialized, and, as a consequence,\nif for some reason, your code use this object while it is uninitialized, then, you will definitely\nhave undefined behaviour and major bugs in your program.\n\nIn the example below, I'm declaring the `age` object again. But this time,\nI do not give it an initial value. The variable is only initialized at\nthe second line of code, where I store the number 25 in this object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\n:::\n\n\n\n\nHaving these points in mind, just remember that you should avoid as much as possible to use `undefined` in your code.\nAlways declare and initialize your objects. Because this gives you much more safety in your program.\nBut in case you really need to declare an object without initializing it... the\n`undefined` keyword is the way to do it in Zig.\n\n\n### There is no such thing as unused objects\n\nEvery object (being constant or variable) that you declare in Zig **must be used in some way**. You can give this object\nto a function call, as a function argument, or, you can use it in another expression\nto calculate the value of another object, or, you can call a method that belongs to this\nparticular object. \n\nIt doesn't matter in which way you use it. As long as you use it.\nIf you try to break this rule, i.e. if your try to declare a object, but not use it,\nthe `zig` compiler will not compile your Zig source code, and it will issue a error\nmessage warning that you have unused objects in your code.\n\nLet's demonstrate this with an example. In the source code below, we declare a constant object\ncalled `age`. If you try to compile a simple Zig program with this line of code below,\nthe compiler will return an error as demonstrated below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\n:::\n\n\n\n\n```\nt.zig:4:11: error: unused local constant\n const age = 15;\n ^~~\n```\n\nEverytime you declare a new object in Zig, you have two choices:\n\n1. you either use the value of this object;\n2. or you explicitly discard the value of the object;\n\nTo explicitly discard the value of any object (constant or variable), all you need to do is to assign\nthis object to an special character in Zig, which is the underscore (`_`).\nWhen you assign an object to a underscore, like in the example below, the `zig` compiler will automatically\ndiscard the value of this particular object.\n\nYou can see in the example below that, this time, the compiler did not\ncomplain about any \"unused constant\", and succesfully compiled our source code.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// It compiles!\nconst age = 15;\n_ = age;\n```\n:::\n\n\n\n\nNow, remember, everytime you assign a particular object to the underscore, this object\nis essentially destroyed. It is discarded by the compiler. This means that you can no longer\nuse this object further in your code. It doesn't exist anymore.\n\nSo if you try to use the constant `age` in the example below, after we discarded it, you\nwill get a loud error message from the compiler (talking about a \"pointless discard\")\nwarning you about this mistake.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// It does not compile.\nconst age = 15;\n_ = age;\n// Using a discarded value!\nstd.debug.print(\"{d}\\n\", .{age + 2});\n```\n:::\n\n\n\n\n```\nt.zig:7:5: error: pointless discard\n of local constant\n```\n\n\nThis same rule applies to variable objects. Every variable object must also be used in\nsome way. And if you assign a variable object to the underscore,\nthis object also get's discarded, and you can no longer use this object.\n\n\n\n### You must mutate every variable objects\n\nEvery variable object that you create in your source code must be mutated at some point.\nIn other words, if you declare an object as a variable\nobject, with the keyword `var`, and you do not change the value of this object\nat some point in the future, the `zig` compiler will detect this,\nand it will raise an error warning you about this mistake.\n\nThe concept behind this is that every object you create in Zig should be preferably a\nconstant object, unless you really need an object whose value will\nchange during the execution of your program.\n\nSo, if I try to declare a variable object such as `where_i_live` below,\nand I do not change the value of this object in some way,\nthe `zig` compiler raises an error message with the phrase \"variable is never mutated\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\n:::\n\n\n\n\n```\nt.zig:7:5: error: local variable is never mutated\nt.zig:7:5: note: consider using 'const'\n```\n\n## Primitive Data Types {#sec-primitive-data-types}\n\nZig have many different primitive data types available for you to use.\nYou can see the full list of available data types at the official\n[Language Reference page](https://ziglang.org/documentation/master/#Primitive-Types)[^lang-data-types].\n\n[^lang-data-types]: .\n\nBut here is a quick list:\n\n- Unsigned integers: `u8`, 8-bit integer; `u16`, 16-bit integer; `u32`, 32-bit integer; `u64`, 64-bit integer; `u128`, 128-bit integer.\n- Signed integers: `i8`, 8-bit integer; `i16`, 16-bit integer; `i32`, 32-bit integer; `i64`, 64-bit integer; `i128`, 128-bit integer.\n- Float number: `f16`, 16-bit floating point; `f32`, 32-bit floating point; `f64`, 64-bit floating point; `f128`, 128-bit floating point;\n- Boolean: `bool`, represents true or false values.\n- C ABI compatible types: `c_long`, `c_char`, `c_short`, `c_ushort`, `c_int`, `c_uint`, and many others.\n- Pointer sized integers: `isize` and `usize`.\n\n\n\n\n\n\n\n## Arrays {#sec-arrays}\n\nYou create arrays in Zig by using a syntax that resembles the C syntax.\nFirst, you specify the size of the array (i.e. the number of elements that will be stored in the array)\nyou want to create inside a pair of brackets.\n\nThen, you specify the data type of the elements that will be stored inside this array.\nAll elements present in an array in Zig must have the same data type. For example, you cannot mix elements\nof type `f32` with elements of type `i32` in the same array.\n\nAfter that, you simply list the values that you want to store in this array inside\na pair of curly braces.\nIn the example below, I am creating two constant objets that contain different arrays.\nThe first object contains an array of 4 integer values, while the second object,\nan array of 3 floating point values.\n\nNow, you should notice that in the object `ls`, I am\nnot explicitly specifying the size of the array inside of the brackets. Instead\nof using a literal value (like the value 4 that I used in the `ns` object), I am\nusing the special character underscore (`_`). This syntax tells the `zig` compiler\nto fill this field with the number of elements listed inside of the curly braces.\nSo, this syntax `[_]` is for lazy (or smart) programmers who leave the job of\ncounting how many elements there are in the curly braces for the compiler.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst ls = [_]f64{432.1, 87.2, 900.05};\n_ = ns; _ = ls;\n```\n:::\n\n\n\n\nIs worth noting that these are static arrays, meaning that\nthey cannot grow in size.\nOnce you declare your array, you cannot change the size of it.\nThis is very commom in low level languages.\nBecause low level languages normally wants to give you (the programmer) full control over memory,\nand the way in which arrays are expanded is tightly related to\nmemory management.\n\n\n### Selecting elements of the array\n\nOne very commom activity is to select specific portions of an array\nyou have in your source code.\nIn Zig, you can select a specific element from your\narray, by simply providing the index of this particular\nelement inside brackets after the object name.\nIn the example below, I am selecting the third element from the\n`ns` array. Notice that Zig is a \"zero-index\" based language,\nlike C, C++, Rust, Python, and many other languages.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\ntry stdout.print(\"{d}\\n\", .{ ns[2] });\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n12\n```\n\n\n:::\n:::\n\n\n\n\nIn contrast, you can also select specific slices (or sections) of your array, by using a\nrange selector. Some programmers also call these selectors of \"slice selectors\",\nand they also exist in Rust, and have the exact same syntax as in Zig.\nAnyway, a range selector is a special expression in Zig that defines\na range of indexes, and it have the syntax `start..end`.\n\nIn the example below, at the second line of code,\nthe `sl` object stores a slice (or a portion) of the\n`ns` array. More precisely, the elements at index 1 and 2\nin the `ns` array. \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst sl = ns[1..3];\n_ = sl;\n```\n:::\n\n\n\n\nWhen you use the `start..end` syntax,\nthe \"end tail\" of the range selector is non-inclusive,\nmeaning that, the index at the end is not included in the range that is\nselected from the array.\nTherefore, the syntax `start..end` actually means `start..end - 1` in practice.\n\nYou can for example, create a slice that goes from the first to the\nlast elements of the array, by using `ar[0..ar.len]` syntax\nIn other words, it is a slice that\naccess all elements in the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ar = [4]u8{48, 24, 12, 6};\nconst sl = ar[0..ar.len];\n_ = sl;\n```\n:::\n\n\n\n\nYou can also use the syntax `start..` in your range selector.\nWhich tells the `zig` compiler to select the portion of the array\nthat begins at the `start` index until the last element of the array.\nIn the example below, we are selecting the range from index 1\nuntil the end of the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst sl = ns[1..];\n_ = sl;\n```\n:::\n\n\n\n\n\n### More on slices\n\nAs we discussed before, in Zig, you can select specific portions of an existing\narray. This is called *slicing* in Zig [@zigguide], because when you select a portion\nof an array, you are creating a slice object from that array.\n\nA slice object is essentially a pointer object accompained by a length number.\nThe pointer object points to the first element in the slice, and the\nlength number tells the `zig` compiler how many elements there are in this slice.\n\n> Slices can be thought of as a pair of `[*]T` (the pointer to the data) and a `usize` (the element count) [@zigguide].\n\nThrough the pointer contained inside the slice you can access the elements (or values)\nthat are inside this range (or portion) that you selected from the original array.\nBut the length number (which you can access through the `len` property of your slice object)\nis the really big improvement (over C arrays for example) that Zig brings to the table here.\n\nBecause with this length number\nthe `zig` compiler can easily check if you are trying to access an index that is out of the bounds of this particular slice,\nor, if you are causing any buffer overflow problems. In the example below,\nwe access the `len` property of the slice `sl`, which tells us that this slice\nhave 2 elements in it.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [4]u8{48, 24, 12, 6};\nconst sl = ns[1..3];\ntry stdout.print(\"{d}\\n\", .{sl.len});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n2\n```\n\n\n:::\n:::\n\n\n\n\n\n### Array operators\n\nThere are two array operators available in Zig that are very useful.\nThe array concatenation operator (`++`), and the array multiplication operator (`**`). As the name suggests,\nthese are array operators.\n\nOne important detail about these two operators is that they work\nonly when both operands have a size (or \"length\") that is compile-time known.\nWe are going to talk more about\nthe differences between \"compile-time known\" and \"runtime known\" at @sec-compile-time.\nBut for now, keep this information in mind, that you cannot use these operators in every situation.\n\nIn summary, the `++` operator creates a new array that is the concatenation,\nof both arrays provided as operands. So, the expression `a ++ b` produces\na new array which contains all the elements from arrays `a` and `b`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst a = [_]u8{1,2,3};\nconst b = [_]u8{4,5};\nconst c = a ++ b;\ntry stdout.print(\"{any}\\n\", .{c});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n{ 1, 2, 3, 4, 5 }\n```\n\n\n:::\n:::\n\n\n\n\nThis `++` operator is particularly useful to concatenate strings together.\nStrings in Zig are described in depth at @sec-zig-strings. In summary, a string object in Zig\nis essentially an arrays of bytes. So, you can use this array concatenation operator\nto effectively concatenate strings together.\n\nIn contrast, the `**` operator is used to replicate an array multiple\ntimes. In other words, the expression `a ** 3` creates a new array\nwhich contains the elements of the array `a` repeated 3 times.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst a = [_]u8{1,2,3};\nconst c = a ** 2;\ntry stdout.print(\"{any}\\n\", .{c});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n{ 1, 2, 3, 1, 2, 3 }\n```\n\n\n:::\n:::\n\n\n\n\n\n## Blocks and scopes {#sec-blocks}\n\nBlocks are created in Zig by a pair of curly braces. A block is just a group of\nexpressions (or statements) contained inside of a pair of curly braces. All of these expressions that\nare contained inside of this pair of curly braces belongs to the same scope.\n\nIn other words, a block just delimits a scope in your code.\nThe objects that you define inside the same block belongs to the same\nscope, and, therefore, are accessible from within this scope.\nAt the same time, these objects are not accessible outside of this scope.\nSo, you could also say that blocks are used to limit the scope of the objects that you create in\nyour source code. In less technical terms, blocks are used to specify where in your source code\nyou can access whatever object you have in your source code.\n\nSo, a block is just a group of expressions contained inside a pair of curly braces.\nAnd every block have it's own scope separated from the others.\nThe body of a function is a classic example of a block. If statements, for and while loops\n(and any other structure in the language that uses the pair of curly braces)\nare also examples of blocks.\n\nThis means that, every if statement, or for loop,\netc., that you create in your source code have it's own separate scope.\nThat is why you can't access the objects that you defined inside\nof your for loop (or if statement) in an outer scope, i.e. a scope outside of the for loop.\nBecause you are trying to access an object that belongs to a scope that is different\nthan your current scope.\n\n\nYou can create blocks within blocks, with multiple levels of nesting.\nYou can also (if you want to) give a label to a particular block, with the colon character (`:`).\nJust write `label:` before you open the pair of curly braces that delimits your block. When you label a block\nin Zig, you can use the `break` keyword to return a value from this block, like as if it\nwas a function's body. You just write the `break` keyword, followed by the block label in the format `:label`,\nand the expression that defines the value that you want to return.\n\nLike in the example below, where we are returning the value from the `y` object\nfrom the block `add_one`, and saving the result inside the `x` object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar y: i32 = 123;\nconst x = add_one: {\n y += 1;\n break :add_one y;\n};\nif (x == 124 and y == 124) {\n try stdout.print(\"Hey!\", .{});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHey!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n\n## How strings work in Zig? {#sec-zig-strings}\n\nThe first project that we are going to build and discuss in this book is a base64 encoder/decoder (@sec-base64).\nBut in order for us to build such a thing, we need to get a better understanding on how strings work in Zig.\nSo let's discuss this specific aspect of Zig.\n\nIn Zig, a string literal value is just a pointer to a null-terminated array of bytes (i.e. the same thing as a C string).\nHowever, a string object in Zig is a little more than just a pointer. A string object\nin Zig is an object of type `[]const u8`, and, this object always contains two things: the\nsame null-terminated array of bytes that you would find in a string literal value, plus a length value.\nEach byte in this \"array of bytes\" is represented by an `u8` value, which is an unsigned 8 bit integer,\nso, it is equivalent to the C data type `unsigned char`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// This is a string literal value:\n\"A literal value\";\n// This is a string object:\nconst object: []const u8 = \"A string object\";\n```\n:::\n\n\n\n\nZig always assumes that this sequence of bytes is UTF-8 encoded. This might not be true for every\nsequence of bytes you have it, but is not really Zig's job to fix the encoding of your strings\n(you can use [`iconv`](https://www.gnu.org/software/libiconv/)[^libiconv] for that).\nToday, most of the text in our modern world, specially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely\nhave problems in Zig.\n\n[^libiconv]: \n\nLet’s take for example the word \"Hello\". In UTF-8, this sequence of characters (H, e, l, l, o)\nis represented by the sequence of decimal numbers 72, 101, 108, 108, 111. In xecadecimal, this\nsequence is `0x48`, `0x65`, `0x6C`, `0x6C`, `0x6F`. So if I take this sequence of hexadecimal values,\nand ask Zig to print this sequence of bytes as a sequence of characters (i.e. a string), then,\nthe text \"Hello\" will be printed into the terminal:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n const bytes = [_]u8{0x48, 0x65, 0x6C, 0x6C, 0x6F};\n try stdout.print(\"{s}\\n\", .{bytes});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHello\n```\n\n\n:::\n:::\n\n\n\n\n\nIf you want to see the actual bytes that represents a string in Zig, you can use\na `for` loop to iterate through each byte in the string, and ask Zig to print each byte as an hexadecimal\nvalue to the terminal. You do that by using a `print()` statement with the `X` formatting specifier,\nlike you would normally do with the [`printf()` function](https://cplusplus.com/reference/cstdio/printf/)[^printfs] in C.\n\n[^printfs]: \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example of string literal in Zig\";\n try stdout.print(\"Bytes that represents the string object: \", .{});\n for (string_object) |byte| {\n try stdout.print(\"{X} \", .{byte});\n }\n try stdout.print(\"\\n\", .{});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nBytes that represents the string object: 54 68 69 \n 73 20 69 73 20 61 6E 20 65 78 61 6D 70 6C 65 20 6F\n F 66 20 73 74 72 69 6E 67 20 6C 69 74 65 72 61 6C 2\n 20 69 6E 20 5A 69 67 \n```\n\n\n:::\n:::\n\n\n\n\n### Strings in C\n\nAt first glance, this looks very similar to how C treats strings as well. In more details, string values\nin C are treated internally as an array of arbitrary bytes, and this array is also null-terminated.\n\nBut one key difference between a Zig string and a C string, is that Zig also stores the length of\nthe array inside the string object. This small detail makes your code safer, because is much\neasier for the Zig compiler to check if you are trying to access an element that is \"out of bounds\", i.e. if\nyour trying to access memory that does not belong to you.\n\nTo achieve this same kind of safety in C, you have to do a lot of work that kind of seems pointless.\nSo getting this kind of safety is not automatic and much harder to do in C. For example, if you want\nto track the length of your string troughout your program in C, then, you first need to loop through\nthe array of bytes that represents this string, and find the null element (`'\\0'`) position to discover\nwhere exactly the array ends, or, in other words, to find how much elements the array of bytes contain.\n\nTo do that, you would need something like this in C. In this example, the C string stored in\nthe object `array` is 25 bytes long:\n\n```c\n#include \nint main() {\n char* array = \"An example of string in C\";\n int index = 0;\n while (1) {\n if (array[index] == '\\0') {\n break;\n }\n index++;\n }\n printf(\"Number of elements in the array: %d\\n\", index);\n}\n```\n\n```\nNumber of elements in the array: 25\n```\n\nBut in Zig, you do not have to do this, because the object already contains a `len`\nfield which stores the length information of the array. As an example, the `string_object` object below is 43 bytes long:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example of string literal in Zig\";\n try stdout.print(\"{d}\\n\", .{string_object.len});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n43\n```\n\n\n:::\n:::\n\n\n\n\n\n### A better look at the object type\n\nNow, we can inspect better the type of objects that Zig create. To check the type of any object in Zig, you can use the\n`@TypeOf()` function. If we look at the type of the `simple_array` object below, you will find that this object\nis a array of 4 elements. Each element is a signed integer of 32 bits which corresponds to the data type `i32` in Zig.\nThat is what an object of type `[4]i32` is.\n\nBut if we look closely at the type of the `string_object` object below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 43 elements (or 43 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[43:0]u8` portion of the type below.\nIn other words, the string stored inside the `string_object` object is 43 bytes long.\nThat is why you have the type `*const [43:0]u8` below.\n\nIn the case of `string_object`, it is a constant pointer (`*const`) because the object `string_object` is declared\nas constant in the source code (in the line `const string_object = ...`). So, if we changed that for some reason, if\nwe declare `string_object` as a variable object (i.e. `var string_object = ...`), then, `string_object` would be\njust a normal pointer to an array of unsigned 8-bit integers (i.e. `* [43:0]u8`).\n\nNow, if we create an pointer to the `simple_array` object, then, we get a constant pointer to an array of 4 elements (`*const [4]i32`),\nwhich is very similar to the type of the `string_object` object. This demonstrates that a string object (or a string literal)\nin Zig is already a pointer to an array.\n\nJust remember that a \"pointer to an array\" is different than an \"array\". So a string object in Zig is a pointer to an array\nof bytes, and not simply an array of bytes.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example of string literal in Zig\";\n const simple_array = [_]i32{1, 2, 3, 4};\n try stdout.print(\"Type of array object: {}\", .{@TypeOf(simple_array)});\n try stdout.print(\n \"Type of string object: {}\",\n .{@TypeOf(string_object)}\n );\n try stdout.print(\n \"Type of a pointer that points to the array object: {}\",\n .{@TypeOf(&simple_array)}\n );\n}\n```\n:::\n\n\n\n\n```\nType of array object: [4]i32\nType of string object: *const [43:0]u8\nType of a pointer that points to\n the array object: *const [4]i32\n```\n\n\n### Byte vs unicode points\n\nIs important to point out that each byte in the array is not necessarily a single character.\nThis fact arises from the difference between a single byte and a single unicode point.\n\nThe encoding UTF-8 works by assigning a number (which is called a unicode point) to each character in\nthe string. For example, the character \"H\" is stored in UTF-8 as the decimal number 72. This means that\nthe number 72 is the unicode point for the character \"H\". Each possible character that can appear in a\nUTF-8 encoded string have its own unicode point.\n\nFor example, the Latin Capital Letter A With Stroke (Ⱥ) is represented by the number (or the unicode point)\n570. However, this decimal number (570) is higher than the maximum number stored inside a single byte, which\nis 255. In other words, the maximum decimal number that can be represented with a single byte is 255. That is why,\nthe unicode point 570 is actually stored inside the computer’s memory as the bytes `C8 BA`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"Ⱥ\";\n try stdout.print(\"Bytes that represents the string object: \", .{});\n for (string_object) |char| {\n try stdout.print(\"{X} \", .{char});\n }\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nBytes that represents the string object: C8 BA \n```\n\n\n:::\n:::\n\n\n\n\n\nThis means that to store the character Ⱥ in an UTF-8 encoded string, we need to use two bytes together\nto represent the number 570. That is why the relationship between bytes and unicode points is not always\n1 to 1. Each unicode point is a single character in the string, but not always a single byte corresponds\nto a single unicode point.\n\nAll of this means that if you loop trough the elements of a string in Zig, you will be looping through the\nbytes that represents that string, and not through the characters of that string. In the Ⱥ example above,\nthe for loop needed two iterations (instead of a single iteration) to print the two bytes that represents this Ⱥ letter.\n\nNow, all english letters (or ASCII letters if you prefer) can be represented by a single byte in UTF-8. As a\nconsequence, if your UTF-8 string contains only english letters (or ASCII letters), then, you are lucky. Because\nthe number of bytes will be equal to the number of characters in that string. In other words, in this specific\nsituation, the relationship between bytes and unicode points is 1 to 1.\n\nBut on the other side, if your string contains other types of letters… for example, you might be working with\ntext data that contains, chinese, japanese or latin letters, then, the number of bytes necessary to represent\nyour UTF-8 string will likely be much higher than the number of characters in that string.\n\nIf you need to iterate through the characters of a string, instead of its bytes, then, you can use the\n`std.unicode.Utf8View` struct to create an iterator that iterates through the unicode points of your string.\n\nIn the example below, we loop through the japanese characters “アメリカ”. Each of the four characters in\nthis string is represented by three bytes. But the for loop iterates four times, one iteration for each\ncharacter/unicode point in this string:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n var utf8 = (\n (try std.unicode.Utf8View.init(\"アメリカ\"))\n .iterator()\n );\n while (utf8.nextCodepointSlice()) |codepoint| {\n try stdout.print(\n \"got codepoint {}\\n\",\n .{std.fmt.fmtSliceHexUpper(codepoint)}\n );\n }\n}\n```\n:::\n\n\n\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecome more interconnected with techology and computers,\nthe data produced by all of this technology becomes one of the most important\n(and also, one of the most dangerous) assets that we have.\n\nThis is probably the main reason why modern low-level programming languages\nhave been giving great attention to safety, specially memory safety, because\nmemory corruption is still the main target for hackers to exploit.\nThe reality is that we don't have an easy solution for this problem.\nFor now, we only have techniques and strategies that mitigates these\nproblems.\n\nAs Richard Feldman explains on his [most recent GOTO conference talk](https://www.youtube.com/watch?v=jIZpKpLCOiU&ab_channel=GOTOConferences)[^gotop]\n, we haven't figured it out yet a way to achieve **true safety in technology**.\nIn other words, we haven't found a way to build software that won't be exploited\nwith 100% certainty. We can greatly reduce the risks of our software being\nexploited, by ensuring memory safety for example. But this is not enough\nto achieve \"true safety\" territory.\n\nBecause even if you write your program in a \"safe language\", hackers can still\nexploit failures in the operational system where your program is running (e.g. maybe the\nsystem where your code is running have a \"backdoor exploit\" that can still\naffect your code in unexpected ways), or also, they can exploit the features\nfrom the architecture of your computer. A recently found exploit\nthat involves memory invalidation through a feature of \"memory tags\"\npresent in ARM chips is an example of that [@exploit1].\n\n[^gotop]: \n\nThe question is: what Zig and other languages have been doing to mitigate this problem?\nIf we take Rust as an example, Rust is, for the most part[^rust-safe], a memory safe\nlanguage by enforcing specific rules to the developer. In other words, the key feature\nof Rust, the *borrow checker*, forces you to follow a specific logic when you are writing\nyour Rust code, and the Rust compiler will always complain everytime you try to go out of this\npattern.\n\n[^rust-safe]: Actually, a lot of existing Rust code is still memory unsafe, because they communicate with external libraries through FFI (*foreign function interface*), which disables the borrow-checker features through the `unsafe` keyword.\n\n\nIn contrast, the Zig language is not a memory safe language by default.\nInstead of forcing the developer to follow a specific rule, the Zig language\nachieves memory safety by offering tools that the developer can use for this purpose.\nIn other words, the `zig` compiler does not obligates you to use such tools.\nBut there is often no reason to not use these tools in your Zig code,\nso you often achieve a similar level of memory safety of Rust in Zig\nby simply using these tools.\n\nThe tools listed below are related to memory safety in Zig. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations phisically close to allocations. This helps you to avoid memory leaks, \"use after free\", and also \"double-free\" problems. Furthermore, it also keeps free operations logically tied to the end of the current scope, which greatly reduces the mental overhead about object lifetime.\n- `errdefer` helps you to garantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and object are non-nullable by default. This helps you to avoid memory problems that might arise from de-referencing null pointers.\n- Zig offers some native types of allocators (called \"testing allocators\") that can detect memory leaks and double-frees. These types of allocators are widely used on unit tests, so they make your unit tests a weapon that you can use to detect memory problems in your code.\n- arrays and slices in Zig have their lengths embedded in the object itself, which makes the `zig` compiler very effective on detecting \"index out-of-range\" type of errors, and avoiding buffer overflows.\n\n\nDespite these features that Zig offers that are related to memory safety issues, the language\nalso have some rules that help you to achieve another type of safety, which is more related to\nprogram logic safety. These rules are:\n\n- pointers and objects are non-nullable by default. Which eliminates an edge case that might break the logic of your program.\n- switch statements must exaust all possible options.\n- the `zig` compiler forces you to handle every possible error.\n\n\n## Other parts of Zig\n\nWe already learned a lot about Zig's syntax, and also, some pretty technical\ndetails about it. Just as a quick recap:\n\n- We talked about how functions are written in Zig at @sec-root-file and @sec-main-file.\n- How to create new objects/identifiers at @sec-root-file and specially at @sec-assignments.\n- How strings work in Zig at @sec-zig-strings.\n- How to use arrays and slices at @sec-arrays.\n- How to import functionality from other Zig modules at @sec-root-file.\n\n\nBut, for now, this amount of knowledge is enough for us to continue with this book.\nLater, over the next chapters we will still talk more about other parts of\nZig's syntax that are also equally important as the other parts. Such as:\n\n\n- How Object-Oriented programming can be done in Zig through *struct declarations* at @sec-structs-and-oop.\n- Basic control flow syntax at @sec-zig-control-flow.\n- Enums at @sec-enum;\n- Pointers and Optionals at @sec-pointer;\n- Error handling with `try` and `catch` at @sec-error-handling;\n- Unit tests at @sec-unittests;\n- Vectors;\n- Build System at @sec-build-system;\n\n\n\n\n", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/docs/Chapters/01-zig-weird.html b/docs/Chapters/01-zig-weird.html index b9c1519..736dcb8 100644 --- a/docs/Chapters/01-zig-weird.html +++ b/docs/Chapters/01-zig-weird.html @@ -287,8 +287,8 @@

Table of contents

  • 1.2 Hello world in Zig
    • 1.2.1 Understanding the project files
    • -
    • 1.2.2 Looking at the root.zig file
    • -
    • 1.2.3 Looking at the main.zig file
    • +
    • 1.2.2 The file root.zig
    • +
    • 1.2.3 The main.zig file
    • 1.2.4 Compiling your source code
    • 1.2.5 Compile and execute at the same time
    • 1.2.6 Compiling the entire project
    • @@ -342,11 +342,11 @@

      1  1. As a consequence, it’s world is still very wild and to be explored. This book is my attempt to help you on your personal journey for understanding and exploring the exciting world of Zig.

      +

      In this chapter, I want to introduce you to the world of Zig. Zig is a very young language that is being actively developed. As a consequence, it’s world is still very wild and to be explored. This book is my attempt to help you on your personal journey for understanding and exploring the exciting world of Zig.

      I assume you have previous experience with some programming language in this book, not necessarily with a low-level one. So, if you have experience with Python, or Javascript, for example, is fine. But, if you do have experience with low-level languages, such as C, C++, or Rust, you will probably learn faster throughout this book.

      1.1 What is Zig?

      -

      Zig is a modern, low-level, and general-purpose programming language. Some programmers interpret Zig as the “modern C language”. It is a simple language like C, but with some modern features.

      +

      Zig is a modern, low-level, and general-purpose programming language. Some programmers think of Zig as a modern and better version of C.

      In the author’s personal interpretation, Zig is tightly connected with “less is more”. Instead of trying to become a modern language by adding more and more features, many of the core improvements that Zig brings to the table are actually about removing annoying and evil behaviours/features from C and C++. In other words, Zig tries to be better by simplifying the language, and by having more consistent and robust behaviour. As a result, analyzing, writing and debugging applications become much easier and simpler in Zig, than it is in C or C++.

      This philosophy becomes clear with the following phrase from the official website of Zig:

      @@ -371,8 +371,8 @@

      1.2.1 Understanding the project files

      After you run the init command from the zig compiler, some new files are created inside of your current directory. First, a “source” (src) directory is created, containing two files, main.zig and root.zig. Each .zig file is a separate Zig module, which is simply a text file that contains some Zig code.

      -

      The main.zig file for example, contains a main() function, which represents the entrypoint of your program. It is where the execution of your program begins. As you would expect from a C, C++, Rust or Go, to build an executabe program in Zig, you also need to declare a main() function in your module. So, the main.zig module represents an executable program written in Zig.

      -

      On the other side, the root.zig module does not contain a main() function. Because it represents a library written in Zig. Libraries are different than executables. They don’t need to have an entrypoint to work. So, you can choose which file (main.zig or root.zig) you want to follow depending on which type of project (executable or library) you want to develop.

      +

      By convention, the main.zig module is where your main function lives. Thus, if you are building an executable program in Zig, you need to declare a main() function, which represents the entrypoint of your program, i.e. it is where the execution of your program begins.

      +

      However, if you are building a library (instead of an executable program), then, the normal procedure is to delete this main.zig file and start with the root.zig module. By convention, the root.zig module is the root source file of your library.

      tree .
      .
       ├── build.zig
      @@ -382,17 +382,17 @@ 

      -

      Now, in addition to the source directory, two other files were created in our working directory: build.zig and build.zig.zon. The first file (build.zig) represents a build script written in Zig. This script is executed when you call the build command from the zig compiler. In other words, this file contain Zig code that executes the necessary steps to build the entire project.

      +

      The ìnit command also creates two additional files in our working directory: build.zig and build.zig.zon. The first file (build.zig) represents a build script written in Zig. This script is executed when you call the build command from the zig compiler. In other words, this file contain Zig code that executes the necessary steps to build the entire project.

      In general, low-level languages normally use a compiler to build your source code into binary executables or binary libraries. Nevertheless, this process of compiling your source code and building binary executables or binary libraries from it, became a real challenge in the programming world, once the projects became bigger and bigger. As a result, programmers created “build systems”, which are a second set of tools designed to make this process of compiling and building complex projects, easier.

      Examples of build systems are CMake, GNU Make, GNU Autoconf and Ninja, which are used to build complex C and C++ projects. With these systems, you can write scripts, which are called “build scripts”. They simply are scripts that describes the necessary steps to compile/build your project.

      However, these are separate tools, that do not belong to C/C++ compilers, like gcc or clang. As a result, in C/C++ projects, you have not only to install and manage your C/C++ compilers, but you also have to install and manage these build systems separately.

      -

      But instead of using a separate build system, in Zig, we use the Zig language itself to write build scripts. In other words, Zig contains a native build system in it. And we can use this build system to write small scripts in Zig, which describes the necessary steps to build/compile our Zig project2. So, everything you need to build a complex Zig project is the zig compiler, and nothing more.

      -

      Now that we described this topic in more depth, let’s focus on the second generated file (build.zig.zon), which is the Zig package manager configuration file, where you can list and manage the dependencies of your project. Yes, Zig has a package manager (like pip in Python, cargo in Rust, or npm in Javascript) called Zon, and this build.zig.zon file is similar to the package.json file in Javascript projects, or, the Pipfile file in Python projects, or the Cargo.toml file in Rust projects.

      +

      But instead of using a separate build system, in Zig, we use the Zig language itself to write build scripts. In other words, Zig contains a native build system in it. And we can use this build system to write small scripts in Zig, which describes the necessary steps to build/compile our Zig project1. So, everything you need to build a complex Zig project is the zig compiler, and nothing more.

      +

      The second generated file (build.zig.zon) is the Zig package manager configuration file, where you can list and manage the dependencies of your project. Yes, Zig has a package manager (like pip in Python, cargo in Rust, or npm in Javascript) called Zon, and this build.zig.zon file is similar to the package.json file in Javascript projects, or, the Pipfile file in Python projects, or the Cargo.toml file in Rust projects.

      -

      1.2.2 Looking at the root.zig file

      -

      Let’s take a look at the root.zig file, and start to analyze some of the syntax of Zig. The first thing that you might notice, is that every line of code that have an expression in it, ends with a semicolon character (;). This is similar syntax to other languages such as C, C++ and Rust, which have the same rule.

      -

      Also, notice the @import() call at the first line. We use this built-in function to import functionality from other Zig modules into our current module. In other words, the @import() function works similarly to the #include pre-processor in C or C++, or, to the import statement in Python or Javascript code. In this example, we are importing the std module, which gives you access to the Zig standard library.

      +

      1.2.2 The file root.zig

      +

      Let’s take a look into the root.zig file. You might have notice that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language2.

      +

      Also, notice the @import() call at the first line. We use this built-in function to import functionality from other Zig modules into our current module. In other words, this @import() function works similarly to the #include pre-processor in C or C++, or, to the import statement in Python or Javascript code. In this example, we are importing the std module, which gives you access to the Zig Standard Library.

      In this root.zig file, we can also see how assignments (i.e. creating new objects) are made in Zig. You can create a new object in Zig by using the following syntax (const|var) name = value;. In the example below, we are creating two constant objects (std and testing). At Section 1.4 we talk more about objects in general.

      const std = @import("std");
      @@ -402,16 +402,16 @@ 

      return a + b; }

      -

      Functions in Zig are declared similarly to functions in Rust, using the fn keyword. In the example above, we are declaring a function called add(), which have two arguments named a and b, and returns a integer number (i32) as result.

      -

      Maybe Zig is not exactly a strongly-typed language, because you do not need necessarily to specify the type of every single object you create across your source code. But you do have to explicitly specify the type of every function argument, and also, the return type of every function you create in Zig. So, at least in function declarations, Zig is a strongly-typed language.

      +

      Functions in Zig are declared using the fn keyword. In this root.zig module, we are declaring a function called add(), which has two arguments named a and b. The function returns an integer of the type i32 as result.

      +

      Zig is not exactly a strongly-typed language. Because you can (if you want to) omit the type of an object in your code, if this type can be derived from the assigned value. But there are other situations where you do need to be explicit. For example, you do have to explicitly specify the type of every single function argument, and also, the return type of every function you create in Zig. So, at least in function declarations, Zig is a strongly-typed language.

      We specify the type of an object or a function argument in Zig, by using a colon character (:) followed by the type after the name of this object/function argument. With the expressions a: i32 and b: i32, we know that, both a and b arguments have type i32, which is a signed 32 bit integer. In this part, the syntax in Zig is identical to the syntax in Rust, which also specifies types by using the colon character.

      -

      Lastly, we have the return type of the function at the end of the line, before we open the curly braces to start writing the function’s body, which, in the example above is again a signed 32 bit integer (i32) value. This specific part is different than it is in Rust. Because in Rust, the return type of a function is specified after an arrow (->). While in Zig, we simply declare the return type directly after the parentheses with the function arguments.

      -

      We also have an export keyword before the function declaration. This keyword is similar to the extern keyword in C. It exposes the function to make it available in the library API.

      +

      Lastly, we have the return type of the function at the end of the line, before we open the curly braces to start writing the function’s body. In the example above, this type is also a signed 32 bit integer (i32) value.

      +

      Notice that we also have an export keyword before the function declaration. This keyword is similar to the extern keyword in C. It exposes the function to make it available in the library API.

      In other words, if you have a project where you are currently building a library for other people to use, you need to expose your functions so that they are available in the library’s API, so that users can use it. If we removed the export keyword from the add() function declaration, then, this function would be no longer exposed in the library object built by the zig compiler.

      -

      Having that in mind, the keyword export is a keyword used in libraries written in Zig. So, if you are not currently writing a library in your project, then, you do not need to care about this keyword.

      +

      Having that in mind, the keyword export is a keyword used in libraries written in Zig. If you are not currently writing a library in your project, then, you do not need to care about this keyword.

      -

      1.2.3 Looking at the main.zig file

      +

      1.2.3 The main.zig file

      Now that we have learned a lot about Zig’s syntax from the root.zig file, let’s take a look at the main.zig file. A lot of the elements we saw in root.zig are also present in main.zig. But we have some other elements that we did not have seen yet, so let’s dive in.

      First, look at the return type of the main() function in this file. We can see a small change. Now, the return type of the function (void) is accompanied by an exclamation mark (!). What this exclamation mark is telling us, is that this main() function might also return an error.

      So, in this example, the main() function can either return void, or, return an error. This is an interesting feature of Zig. If you write a function, and, something inside of the body of this function might return an error, then, you are forced to:

      @@ -927,8 +927,8 @@


        -
      1. New programming languages in general, take years and years to be developed.↩︎

      2. -
      3. https://ziglang.org/learn/overview/#zig-build-system.↩︎

      4. +
      5. https://ziglang.org/learn/overview/#zig-build-system.↩︎

      6. +
      7. https://en.wikipedia.org/wiki/List_of_C-family_programming_languages↩︎

      8. https://github.com/ziglang/zig/tree/master/lib/std↩︎

      9. https://github.com/oven-sh/bun.↩︎

      10. https://github.com/hexops/mach↩︎

      11. diff --git a/docs/search.json b/docs/search.json index 73e0dea..702ce40 100644 --- a/docs/search.json +++ b/docs/search.json @@ -84,7 +84,7 @@ "href": "Chapters/01-zig-weird.html", "title": "1  Introducing Zig", "section": "", - "text": "1.1 What is Zig?\nZig is a modern, low-level, and general-purpose programming language. Some programmers interpret Zig as the “modern C language”. It is a simple language like C, but with some modern features.\nIn the author’s personal interpretation, Zig is tightly connected with “less is more”. Instead of trying to become a modern language by adding more and more features, many of the core improvements that Zig brings to the table are actually about removing annoying and evil behaviours/features from C and C++. In other words, Zig tries to be better by simplifying the language, and by having more consistent and robust behaviour. As a result, analyzing, writing and debugging applications become much easier and simpler in Zig, than it is in C or C++.\nThis philosophy becomes clear with the following phrase from the official website of Zig:\nThis phrase is specially true for C++ programmers. Because C++ is a gigantic language, with tons of features, and also, there are lots of different “flavors of C++”. These elements are what makes C++ so much complex and hard to learn. Zig tries to go in the opposite direction. Zig is a very simple language, more closely related to other simple languages such as C and Go.\nThe phrase above is still important for C programmers too. Because, even C being a simple language, it is still hard sometimes to read and understand C code. For example, pre-processor macros in C are an evil source of confusion. They really makes it hard sometimes to debug C programs. Because macros are essentially a second language embedded in C that obscures your C code. With macros, you are no longer 100% sure about which pieces of code are being sent to the compiler. It obscures the actual source code that you wrote.\nYou don’t have macros in Zig. In Zig, the code you write, is the actual code that get’s compiled by the compiler. You don’t have evil features that obscures you code. You also don’t have hidden control flow happening behind the scenes. And, you also don’t have functions or operators from the standard library that make hidden memory allocations behind your back.\nBy being a simpler language, Zig becomes much more clear and easier to read/write, but at the same time, it also achieves a much more robust state, with more consistent behaviour in edge situations. Once again, less is more.", + "text": "1.1 What is Zig?\nZig is a modern, low-level, and general-purpose programming language. Some programmers think of Zig as a modern and better version of C.\nIn the author’s personal interpretation, Zig is tightly connected with “less is more”. Instead of trying to become a modern language by adding more and more features, many of the core improvements that Zig brings to the table are actually about removing annoying and evil behaviours/features from C and C++. In other words, Zig tries to be better by simplifying the language, and by having more consistent and robust behaviour. As a result, analyzing, writing and debugging applications become much easier and simpler in Zig, than it is in C or C++.\nThis philosophy becomes clear with the following phrase from the official website of Zig:\nThis phrase is specially true for C++ programmers. Because C++ is a gigantic language, with tons of features, and also, there are lots of different “flavors of C++”. These elements are what makes C++ so much complex and hard to learn. Zig tries to go in the opposite direction. Zig is a very simple language, more closely related to other simple languages such as C and Go.\nThe phrase above is still important for C programmers too. Because, even C being a simple language, it is still hard sometimes to read and understand C code. For example, pre-processor macros in C are an evil source of confusion. They really makes it hard sometimes to debug C programs. Because macros are essentially a second language embedded in C that obscures your C code. With macros, you are no longer 100% sure about which pieces of code are being sent to the compiler. It obscures the actual source code that you wrote.\nYou don’t have macros in Zig. In Zig, the code you write, is the actual code that get’s compiled by the compiler. You don’t have evil features that obscures you code. You also don’t have hidden control flow happening behind the scenes. And, you also don’t have functions or operators from the standard library that make hidden memory allocations behind your back.\nBy being a simpler language, Zig becomes much more clear and easier to read/write, but at the same time, it also achieves a much more robust state, with more consistent behaviour in edge situations. Once again, less is more.", "crumbs": [ "1  Introducing Zig" ] @@ -104,7 +104,7 @@ "href": "Chapters/01-zig-weird.html#hello-world-in-zig", "title": "1  Introducing Zig", "section": "1.2 Hello world in Zig", - "text": "1.2 Hello world in Zig\nWe begin our journey in Zig by creating a small “Hello World” program. To start a new Zig project in your computer, you simply call the init command from the zig compiler. Just create a new directory in your computer, then, init a new Zig project inside this directory, like this:\nmkdir hello_world\ncd hello_world\nzig init\ninfo: created build.zig\ninfo: created build.zig.zon\ninfo: created src/main.zig\ninfo: created src/root.zig\ninfo: see `zig build --help` for a menu of options\n\n1.2.1 Understanding the project files\nAfter you run the init command from the zig compiler, some new files are created inside of your current directory. First, a “source” (src) directory is created, containing two files, main.zig and root.zig. Each .zig file is a separate Zig module, which is simply a text file that contains some Zig code.\nThe main.zig file for example, contains a main() function, which represents the entrypoint of your program. It is where the execution of your program begins. As you would expect from a C, C++, Rust or Go, to build an executabe program in Zig, you also need to declare a main() function in your module. So, the main.zig module represents an executable program written in Zig.\nOn the other side, the root.zig module does not contain a main() function. Because it represents a library written in Zig. Libraries are different than executables. They don’t need to have an entrypoint to work. So, you can choose which file (main.zig or root.zig) you want to follow depending on which type of project (executable or library) you want to develop.\ntree .\n.\n├── build.zig\n├── build.zig.zon\n└── src\n ├── main.zig\n └── root.zig\n\n1 directory, 4 files\nNow, in addition to the source directory, two other files were created in our working directory: build.zig and build.zig.zon. The first file (build.zig) represents a build script written in Zig. This script is executed when you call the build command from the zig compiler. In other words, this file contain Zig code that executes the necessary steps to build the entire project.\nIn general, low-level languages normally use a compiler to build your source code into binary executables or binary libraries. Nevertheless, this process of compiling your source code and building binary executables or binary libraries from it, became a real challenge in the programming world, once the projects became bigger and bigger. As a result, programmers created “build systems”, which are a second set of tools designed to make this process of compiling and building complex projects, easier.\nExamples of build systems are CMake, GNU Make, GNU Autoconf and Ninja, which are used to build complex C and C++ projects. With these systems, you can write scripts, which are called “build scripts”. They simply are scripts that describes the necessary steps to compile/build your project.\nHowever, these are separate tools, that do not belong to C/C++ compilers, like gcc or clang. As a result, in C/C++ projects, you have not only to install and manage your C/C++ compilers, but you also have to install and manage these build systems separately.\nBut instead of using a separate build system, in Zig, we use the Zig language itself to write build scripts. In other words, Zig contains a native build system in it. And we can use this build system to write small scripts in Zig, which describes the necessary steps to build/compile our Zig project2. So, everything you need to build a complex Zig project is the zig compiler, and nothing more.\nNow that we described this topic in more depth, let’s focus on the second generated file (build.zig.zon), which is the Zig package manager configuration file, where you can list and manage the dependencies of your project. Yes, Zig has a package manager (like pip in Python, cargo in Rust, or npm in Javascript) called Zon, and this build.zig.zon file is similar to the package.json file in Javascript projects, or, the Pipfile file in Python projects, or the Cargo.toml file in Rust projects.\n\n\n1.2.2 Looking at the root.zig file\nLet’s take a look at the root.zig file, and start to analyze some of the syntax of Zig. The first thing that you might notice, is that every line of code that have an expression in it, ends with a semicolon character (;). This is similar syntax to other languages such as C, C++ and Rust, which have the same rule.\nAlso, notice the @import() call at the first line. We use this built-in function to import functionality from other Zig modules into our current module. In other words, the @import() function works similarly to the #include pre-processor in C or C++, or, to the import statement in Python or Javascript code. In this example, we are importing the std module, which gives you access to the Zig standard library.\nIn this root.zig file, we can also see how assignments (i.e. creating new objects) are made in Zig. You can create a new object in Zig by using the following syntax (const|var) name = value;. In the example below, we are creating two constant objects (std and testing). At Section 1.4 we talk more about objects in general.\n\nconst std = @import(\"std\");\nconst testing = std.testing;\n\nexport fn add(a: i32, b: i32) i32 {\n return a + b;\n}\n\nFunctions in Zig are declared similarly to functions in Rust, using the fn keyword. In the example above, we are declaring a function called add(), which have two arguments named a and b, and returns a integer number (i32) as result.\nMaybe Zig is not exactly a strongly-typed language, because you do not need necessarily to specify the type of every single object you create across your source code. But you do have to explicitly specify the type of every function argument, and also, the return type of every function you create in Zig. So, at least in function declarations, Zig is a strongly-typed language.\nWe specify the type of an object or a function argument in Zig, by using a colon character (:) followed by the type after the name of this object/function argument. With the expressions a: i32 and b: i32, we know that, both a and b arguments have type i32, which is a signed 32 bit integer. In this part, the syntax in Zig is identical to the syntax in Rust, which also specifies types by using the colon character.\nLastly, we have the return type of the function at the end of the line, before we open the curly braces to start writing the function’s body, which, in the example above is again a signed 32 bit integer (i32) value. This specific part is different than it is in Rust. Because in Rust, the return type of a function is specified after an arrow (->). While in Zig, we simply declare the return type directly after the parentheses with the function arguments.\nWe also have an export keyword before the function declaration. This keyword is similar to the extern keyword in C. It exposes the function to make it available in the library API.\nIn other words, if you have a project where you are currently building a library for other people to use, you need to expose your functions so that they are available in the library’s API, so that users can use it. If we removed the export keyword from the add() function declaration, then, this function would be no longer exposed in the library object built by the zig compiler.\nHaving that in mind, the keyword export is a keyword used in libraries written in Zig. So, if you are not currently writing a library in your project, then, you do not need to care about this keyword.\n\n\n1.2.3 Looking at the main.zig file\nNow that we have learned a lot about Zig’s syntax from the root.zig file, let’s take a look at the main.zig file. A lot of the elements we saw in root.zig are also present in main.zig. But we have some other elements that we did not have seen yet, so let’s dive in.\nFirst, look at the return type of the main() function in this file. We can see a small change. Now, the return type of the function (void) is accompanied by an exclamation mark (!). What this exclamation mark is telling us, is that this main() function might also return an error.\nSo, in this example, the main() function can either return void, or, return an error. This is an interesting feature of Zig. If you write a function, and, something inside of the body of this function might return an error, then, you are forced to:\n\neither add the exclamation mark to the return type of the function, to make it clear that this function might return an error.\nor explicitly handle this error that might occur inside the function, to make sure that, if this error does happen, you are prepared, and your function will no longer return an error because you handled the error inside your function.\n\nIn most programming languages, we normally handle (or deals with) an error through a try catch pattern, and Zig, this is no different. But, if we look at the main() function below, you can see that we do have a try keyword in the 5th line. But we do not have a catch keyword in this code.\nThis means that, we are using the keyword try to execute a code that might return an error, which is the stdout.print() expression. But because we do not have a catch keyword in this line, we are not treating (or dealing with) this error. So, if this expression do return an error, we are not catching and solving this error in any way. That is why the exclamation mark was added to the return type of the function.\nSo, in essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword do nothing essentially. It simply passes this value forward. But, if the expression do return an error, then, the try keyword will unwrap and return this error from the function, and also print it’s stack trace to stderr.\nThis might sound weird to you, if you come from a high-level language. Because in high-level languages, such as Python, if an error occurs somewhere, this error is automatically returned and the execution of your program will automatically stops, even if you don’t want to stop the execution. You are obligated to face the error.\nBut if you come from a low-level language, then, maybe, this idea do not sound so weird or distant to you. Because in C for example, normally functions doesn’t raise errors, or, they normally don’t stop the execution. In C, error handling is done by constantly checking the return value of the function. So, you run the function, and then, you use an if statement to check if the function returned a value that is valid, or, if it returned an error. If an error was returned from the function, then, the if statement will execute some code that fixes this error.\nSo, at least for C programmers, they do need to write a lot of if statements to constantly check for errors around their code. And because of that, this simple feature from Zig, might be extraordinary for them. Because this try keyword can automatically unwrap the error, and warn you about this error, and let you deal with it, without any extra work from the programmer.\n\nconst std = @import(\"std\");\n\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n\nNow, another thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. This keyword means “public”. It marks the main() function as a public function from this module.\nIn other words, every function that you declare in your Zig module is, by default, a private (or “static”) function that belongs to this Zig module, and can only be used (or called) from within this same module. Unless, you explicitly mark this function as a public function with the pub keyword. This means that the pub keyword in Zig do essentially the opposite of what the static keyword do in C/C++.\nBy making a function “public”, you allow other Zig modules to access and call this function, and use it for they own purposes. all these other Zig modules need to do is, to import your module with the @import() built-in function. Then, they get access to all public functions that are present in your Zig module.\n\n\n1.2.4 Compiling your source code\nYou can compile your Zig modules into a binary executable by running the build-exe command from the zig compiler. You simply list all the Zig modules that you want to build after the build-exe command, separated by spaces. In the example below, we are compiling the module main.zig.\nzig build-exe src/main.zig\nSince we are building an executable, the zig compiler will look for a main() function declared in any of the files that you list after the build-exe command. If the compiler does not find a main() function declared somewhere, a compilation error will be raised, warning about this mistake.\nThe zig compiler also offers a build-lib and build-obj commands, which work the exact same way as the build-exe command. The only difference is that, they compile your Zig modules into a portale C ABI library, or, into object files, respectively.\nIn the case of the build-exe command, a binary executable file is created by the zig compiler in the root directory of your project. If we take a look now at the contents of our current directory, with a simple ls command, we can see the binary file called main that was created by the compiler.\nls\nbuild.zig build.zig.zon main src\nIf I execute this binary executable, I get the “Hello World” message in the terminal , as we expected.\n./main\nHello, world!\n\n\n1.2.5 Compile and execute at the same time\nOn the previous section, I presented the zig build-exe command, which compiles Zig modules into an executable file. However, this means that, in order to execute the executable file, we have to run two different commands. First, the zig build-exe command, and then, we call the executable file created by the compiler.\nBut what if we wanted to perform these two steps, all at once, in a single command? We can do that by using the zig run command.\nzig run src/main.zig\nHello, world!\n\n\n1.2.6 Compiling the entire project\nJust as I described at Section 1.2.1, as our project grows in size and complexity, we usually prefer to organize the compilation and build process of the project into a build script, using some sort of “build system”.\nIn other words, as our project grows in size and complexity, the build-exe, build-lib and build-obj commands become harder to use directly. Because then, we start to list multiple and multiple modules at the same time. We also start to add built-in compilation flags to customize the build process for our needs, etc. It becomes a lot of work to write the necessary commands by hand.\nIn C/C++ projects, programmers normally opt to use CMake, Ninja, Makefile or configure scripts to organize this process. However, in Zig, we have a native build system in the language itself. So, we can write build scripts in Zig to compile and build Zig projects. Then, all we need to do, is to call the zig build command to build our project.\nSo, when you execute the zig build command, the zig compiler will search for a Zig module named build.zig inside your current directory, which should be your build script, containing the necessary code to compile and build your project. If the compiler do find this build.zig file in your directory, then, the compiler will essentially execute a zig run command over this build.zig file, to compile and execute this build script, which in turn, will compile and build your entire project.\nzig build\nAfter you execute this “build project” command, a zig-out directory is created in the root of your project directory, where you can find the binary executables and libraries created from your Zig modules accordingly to the build commands that you specified at build.zig. We will talk more about the build system in Zig latter in this book.\nIn the example below, I’m executing the binary executable named hello_world that was generated by the compiler after the zig build command.\n./zig-out/bin/hello_world\nHello, world!", + "text": "1.2 Hello world in Zig\nWe begin our journey in Zig by creating a small “Hello World” program. To start a new Zig project in your computer, you simply call the init command from the zig compiler. Just create a new directory in your computer, then, init a new Zig project inside this directory, like this:\nmkdir hello_world\ncd hello_world\nzig init\ninfo: created build.zig\ninfo: created build.zig.zon\ninfo: created src/main.zig\ninfo: created src/root.zig\ninfo: see `zig build --help` for a menu of options\n\n1.2.1 Understanding the project files\nAfter you run the init command from the zig compiler, some new files are created inside of your current directory. First, a “source” (src) directory is created, containing two files, main.zig and root.zig. Each .zig file is a separate Zig module, which is simply a text file that contains some Zig code.\nBy convention, the main.zig module is where your main function lives. Thus, if you are building an executable program in Zig, you need to declare a main() function, which represents the entrypoint of your program, i.e. it is where the execution of your program begins.\nHowever, if you are building a library (instead of an executable program), then, the normal procedure is to delete this main.zig file and start with the root.zig module. By convention, the root.zig module is the root source file of your library.\ntree .\n.\n├── build.zig\n├── build.zig.zon\n└── src\n ├── main.zig\n └── root.zig\n\n1 directory, 4 files\nThe ìnit command also creates two additional files in our working directory: build.zig and build.zig.zon. The first file (build.zig) represents a build script written in Zig. This script is executed when you call the build command from the zig compiler. In other words, this file contain Zig code that executes the necessary steps to build the entire project.\nIn general, low-level languages normally use a compiler to build your source code into binary executables or binary libraries. Nevertheless, this process of compiling your source code and building binary executables or binary libraries from it, became a real challenge in the programming world, once the projects became bigger and bigger. As a result, programmers created “build systems”, which are a second set of tools designed to make this process of compiling and building complex projects, easier.\nExamples of build systems are CMake, GNU Make, GNU Autoconf and Ninja, which are used to build complex C and C++ projects. With these systems, you can write scripts, which are called “build scripts”. They simply are scripts that describes the necessary steps to compile/build your project.\nHowever, these are separate tools, that do not belong to C/C++ compilers, like gcc or clang. As a result, in C/C++ projects, you have not only to install and manage your C/C++ compilers, but you also have to install and manage these build systems separately.\nBut instead of using a separate build system, in Zig, we use the Zig language itself to write build scripts. In other words, Zig contains a native build system in it. And we can use this build system to write small scripts in Zig, which describes the necessary steps to build/compile our Zig project1. So, everything you need to build a complex Zig project is the zig compiler, and nothing more.\nThe second generated file (build.zig.zon) is the Zig package manager configuration file, where you can list and manage the dependencies of your project. Yes, Zig has a package manager (like pip in Python, cargo in Rust, or npm in Javascript) called Zon, and this build.zig.zon file is similar to the package.json file in Javascript projects, or, the Pipfile file in Python projects, or the Cargo.toml file in Rust projects.\n\n\n1.2.2 The file root.zig\nLet’s take a look into the root.zig file. You might have notice that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language2.\nAlso, notice the @import() call at the first line. We use this built-in function to import functionality from other Zig modules into our current module. In other words, this @import() function works similarly to the #include pre-processor in C or C++, or, to the import statement in Python or Javascript code. In this example, we are importing the std module, which gives you access to the Zig Standard Library.\nIn this root.zig file, we can also see how assignments (i.e. creating new objects) are made in Zig. You can create a new object in Zig by using the following syntax (const|var) name = value;. In the example below, we are creating two constant objects (std and testing). At Section 1.4 we talk more about objects in general.\n\nconst std = @import(\"std\");\nconst testing = std.testing;\n\nexport fn add(a: i32, b: i32) i32 {\n return a + b;\n}\n\nFunctions in Zig are declared using the fn keyword. In this root.zig module, we are declaring a function called add(), which has two arguments named a and b. The function returns an integer of the type i32 as result.\nZig is not exactly a strongly-typed language. Because you can (if you want to) omit the type of an object in your code, if this type can be derived from the assigned value. But there are other situations where you do need to be explicit. For example, you do have to explicitly specify the type of every single function argument, and also, the return type of every function you create in Zig. So, at least in function declarations, Zig is a strongly-typed language.\nWe specify the type of an object or a function argument in Zig, by using a colon character (:) followed by the type after the name of this object/function argument. With the expressions a: i32 and b: i32, we know that, both a and b arguments have type i32, which is a signed 32 bit integer. In this part, the syntax in Zig is identical to the syntax in Rust, which also specifies types by using the colon character.\nLastly, we have the return type of the function at the end of the line, before we open the curly braces to start writing the function’s body. In the example above, this type is also a signed 32 bit integer (i32) value.\nNotice that we also have an export keyword before the function declaration. This keyword is similar to the extern keyword in C. It exposes the function to make it available in the library API.\nIn other words, if you have a project where you are currently building a library for other people to use, you need to expose your functions so that they are available in the library’s API, so that users can use it. If we removed the export keyword from the add() function declaration, then, this function would be no longer exposed in the library object built by the zig compiler.\nHaving that in mind, the keyword export is a keyword used in libraries written in Zig. If you are not currently writing a library in your project, then, you do not need to care about this keyword.\n\n\n1.2.3 The main.zig file\nNow that we have learned a lot about Zig’s syntax from the root.zig file, let’s take a look at the main.zig file. A lot of the elements we saw in root.zig are also present in main.zig. But we have some other elements that we did not have seen yet, so let’s dive in.\nFirst, look at the return type of the main() function in this file. We can see a small change. Now, the return type of the function (void) is accompanied by an exclamation mark (!). What this exclamation mark is telling us, is that this main() function might also return an error.\nSo, in this example, the main() function can either return void, or, return an error. This is an interesting feature of Zig. If you write a function, and, something inside of the body of this function might return an error, then, you are forced to:\n\neither add the exclamation mark to the return type of the function, to make it clear that this function might return an error.\nor explicitly handle this error that might occur inside the function, to make sure that, if this error does happen, you are prepared, and your function will no longer return an error because you handled the error inside your function.\n\nIn most programming languages, we normally handle (or deals with) an error through a try catch pattern, and Zig, this is no different. But, if we look at the main() function below, you can see that we do have a try keyword in the 5th line. But we do not have a catch keyword in this code.\nThis means that, we are using the keyword try to execute a code that might return an error, which is the stdout.print() expression. But because we do not have a catch keyword in this line, we are not treating (or dealing with) this error. So, if this expression do return an error, we are not catching and solving this error in any way. That is why the exclamation mark was added to the return type of the function.\nSo, in essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword do nothing essentially. It simply passes this value forward. But, if the expression do return an error, then, the try keyword will unwrap and return this error from the function, and also print it’s stack trace to stderr.\nThis might sound weird to you, if you come from a high-level language. Because in high-level languages, such as Python, if an error occurs somewhere, this error is automatically returned and the execution of your program will automatically stops, even if you don’t want to stop the execution. You are obligated to face the error.\nBut if you come from a low-level language, then, maybe, this idea do not sound so weird or distant to you. Because in C for example, normally functions doesn’t raise errors, or, they normally don’t stop the execution. In C, error handling is done by constantly checking the return value of the function. So, you run the function, and then, you use an if statement to check if the function returned a value that is valid, or, if it returned an error. If an error was returned from the function, then, the if statement will execute some code that fixes this error.\nSo, at least for C programmers, they do need to write a lot of if statements to constantly check for errors around their code. And because of that, this simple feature from Zig, might be extraordinary for them. Because this try keyword can automatically unwrap the error, and warn you about this error, and let you deal with it, without any extra work from the programmer.\n\nconst std = @import(\"std\");\n\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n\nNow, another thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. This keyword means “public”. It marks the main() function as a public function from this module.\nIn other words, every function that you declare in your Zig module is, by default, a private (or “static”) function that belongs to this Zig module, and can only be used (or called) from within this same module. Unless, you explicitly mark this function as a public function with the pub keyword. This means that the pub keyword in Zig do essentially the opposite of what the static keyword do in C/C++.\nBy making a function “public”, you allow other Zig modules to access and call this function, and use it for they own purposes. all these other Zig modules need to do is, to import your module with the @import() built-in function. Then, they get access to all public functions that are present in your Zig module.\n\n\n1.2.4 Compiling your source code\nYou can compile your Zig modules into a binary executable by running the build-exe command from the zig compiler. You simply list all the Zig modules that you want to build after the build-exe command, separated by spaces. In the example below, we are compiling the module main.zig.\nzig build-exe src/main.zig\nSince we are building an executable, the zig compiler will look for a main() function declared in any of the files that you list after the build-exe command. If the compiler does not find a main() function declared somewhere, a compilation error will be raised, warning about this mistake.\nThe zig compiler also offers a build-lib and build-obj commands, which work the exact same way as the build-exe command. The only difference is that, they compile your Zig modules into a portale C ABI library, or, into object files, respectively.\nIn the case of the build-exe command, a binary executable file is created by the zig compiler in the root directory of your project. If we take a look now at the contents of our current directory, with a simple ls command, we can see the binary file called main that was created by the compiler.\nls\nbuild.zig build.zig.zon main src\nIf I execute this binary executable, I get the “Hello World” message in the terminal , as we expected.\n./main\nHello, world!\n\n\n1.2.5 Compile and execute at the same time\nOn the previous section, I presented the zig build-exe command, which compiles Zig modules into an executable file. However, this means that, in order to execute the executable file, we have to run two different commands. First, the zig build-exe command, and then, we call the executable file created by the compiler.\nBut what if we wanted to perform these two steps, all at once, in a single command? We can do that by using the zig run command.\nzig run src/main.zig\nHello, world!\n\n\n1.2.6 Compiling the entire project\nJust as I described at Section 1.2.1, as our project grows in size and complexity, we usually prefer to organize the compilation and build process of the project into a build script, using some sort of “build system”.\nIn other words, as our project grows in size and complexity, the build-exe, build-lib and build-obj commands become harder to use directly. Because then, we start to list multiple and multiple modules at the same time. We also start to add built-in compilation flags to customize the build process for our needs, etc. It becomes a lot of work to write the necessary commands by hand.\nIn C/C++ projects, programmers normally opt to use CMake, Ninja, Makefile or configure scripts to organize this process. However, in Zig, we have a native build system in the language itself. So, we can write build scripts in Zig to compile and build Zig projects. Then, all we need to do, is to call the zig build command to build our project.\nSo, when you execute the zig build command, the zig compiler will search for a Zig module named build.zig inside your current directory, which should be your build script, containing the necessary code to compile and build your project. If the compiler do find this build.zig file in your directory, then, the compiler will essentially execute a zig run command over this build.zig file, to compile and execute this build script, which in turn, will compile and build your entire project.\nzig build\nAfter you execute this “build project” command, a zig-out directory is created in the root of your project directory, where you can find the binary executables and libraries created from your Zig modules accordingly to the build commands that you specified at build.zig. We will talk more about the build system in Zig latter in this book.\nIn the example below, I’m executing the binary executable named hello_world that was generated by the compiler after the zig build command.\n./zig-out/bin/hello_world\nHello, world!", "crumbs": [ "1  Introducing Zig" ] @@ -194,7 +194,7 @@ "href": "Chapters/01-zig-weird.html#footnotes", "title": "1  Introducing Zig", "section": "", - "text": "New programming languages in general, take years and years to be developed.↩︎\nhttps://ziglang.org/learn/overview/#zig-build-system.↩︎\nhttps://github.com/ziglang/zig/tree/master/lib/std↩︎\nhttps://github.com/oven-sh/bun.↩︎\nhttps://github.com/hexops/mach↩︎\nhttps://github.com/cgbur/llama2.zig/tree/main↩︎\nhttps://github.com/tigerbeetle/tigerbeetle↩︎\nhttps://github.com/Hejsil/zig-clap↩︎\nhttps://github.com/capy-ui/capy↩︎\nhttps://github.com/zigtools/zls↩︎\nhttps://github.com/mitchellh/libxev↩︎\nhttps://codeberg.org/ziglings/exercises/.↩︎\nhttps://www.youtube.com/watch?v=OPuztQfM3Fg&t=2524s&ab_channel=TheVimeagen.↩︎\nhttps://adventofcode.com/↩︎\nhttps://ziglang.org/documentation/master/#Primitive-Types.↩︎\nhttps://www.gnu.org/software/libiconv/↩︎\nhttps://cplusplus.com/reference/cstdio/printf/↩︎\nhttps://www.youtube.com/watch?v=jIZpKpLCOiU&ab_channel=GOTOConferences↩︎\nActually, a lot of existing Rust code is still memory unsafe, because they communicate with external libraries through FFI (foreign function interface), which disables the borrow-checker features through the unsafe keyword.↩︎", + "text": "https://ziglang.org/learn/overview/#zig-build-system.↩︎\nhttps://en.wikipedia.org/wiki/List_of_C-family_programming_languages↩︎\nhttps://github.com/ziglang/zig/tree/master/lib/std↩︎\nhttps://github.com/oven-sh/bun.↩︎\nhttps://github.com/hexops/mach↩︎\nhttps://github.com/cgbur/llama2.zig/tree/main↩︎\nhttps://github.com/tigerbeetle/tigerbeetle↩︎\nhttps://github.com/Hejsil/zig-clap↩︎\nhttps://github.com/capy-ui/capy↩︎\nhttps://github.com/zigtools/zls↩︎\nhttps://github.com/mitchellh/libxev↩︎\nhttps://codeberg.org/ziglings/exercises/.↩︎\nhttps://www.youtube.com/watch?v=OPuztQfM3Fg&t=2524s&ab_channel=TheVimeagen.↩︎\nhttps://adventofcode.com/↩︎\nhttps://ziglang.org/documentation/master/#Primitive-Types.↩︎\nhttps://www.gnu.org/software/libiconv/↩︎\nhttps://cplusplus.com/reference/cstdio/printf/↩︎\nhttps://www.youtube.com/watch?v=jIZpKpLCOiU&ab_channel=GOTOConferences↩︎\nActually, a lot of existing Rust code is still memory unsafe, because they communicate with external libraries through FFI (foreign function interface), which disables the borrow-checker features through the unsafe keyword.↩︎", "crumbs": [ "1  Introducing Zig" ]