From 5d01c254c8de65bfbcb1155d2d7531cc4898dfb4 Mon Sep 17 00:00:00 2001
From: KorryKatti <143781663+KorryKatti@users.noreply.github.com>
Date: Mon, 18 Nov 2024 23:17:25 +0530
Subject: [PATCH 001/151] Update 01-zig-weird.qmd
---
Chapters/01-zig-weird.qmd | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd
index 7f564f7e..c1539784 100644
--- a/Chapters/01-zig-weird.qmd
+++ b/Chapters/01-zig-weird.qmd
@@ -359,6 +359,26 @@ zig run src/main.zig
```
Hello, world!
```
+### Important Note for Windows Users
+
+On Windows, certain global initializations that rely on runtime resources, such as `std.io.getStdOut().writer()`, may fail. This happens because global variables in Zig are initialized at **compile-time**, and operations like accessing `stdout` or opening files are restricted to runtime.
+
+### Example
+**Incorrect:**
+```{zig}
+#| eval: false
+#| auto_main: false
+const stdout = std.io.getStdOut().writer(); // ❌ Compile-time error on Windows
+```
+Correct:
+
+```{zig}
+#| eval: false
+#| auto_main: false
+pub fn main() void {
+ const stdout = std.io.getStdOut().writer(); // ✅ Runtime initialization
+}
+```
### Compiling the entire project {#sec-compile-project}
From 96e86e97db6c11a9af399cb8162ceafa5eb559c2 Mon Sep 17 00:00:00 2001
From: KorryKatti <143781663+KorryKatti@users.noreply.github.com>
Date: Thu, 21 Nov 2024 21:21:46 +0530
Subject: [PATCH 002/151] Update 03-structs.qmd
---
Chapters/03-structs.qmd | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/Chapters/03-structs.qmd b/Chapters/03-structs.qmd
index 11b4861f..ef7c740c 100644
--- a/Chapters/03-structs.qmd
+++ b/Chapters/03-structs.qmd
@@ -282,15 +282,18 @@ switch statement two times, before ending at the `3` branch.
#| auto_main: false
#| build_type: "lib"
#| eval: false
-xsw: switch (@as(u8, 1)) {
- 1 => {
- try stdout.print("First branch\n", .{});
- continue :xsw 2;
- },
- 2 => continue :xsw 3,
- 3 => return,
- 4 => {},
-}
+ xsw: switch (@as(u8, 1)) {
+ 1 => {
+ try stdout.print("First branch\n", .{});
+ continue :xsw 2;
+ },
+ 2 => continue :xsw 3,
+ 3 => return,
+ 4 => {},
+ else => {
+ try stdout.print("Unmatched case, value: {d}\n", .{@as(u8, 1)});
+ },
+ }
```
From 426b59ba234ad2eb27ffef0a089d90ac9ed824d0 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 24 Nov 2024 20:17:42 -0300
Subject: [PATCH 003/151] Fix indentation
---
Chapters/03-structs.qmd | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/Chapters/03-structs.qmd b/Chapters/03-structs.qmd
index ef7c740c..1dd3552d 100644
--- a/Chapters/03-structs.qmd
+++ b/Chapters/03-structs.qmd
@@ -282,18 +282,20 @@ switch statement two times, before ending at the `3` branch.
#| auto_main: false
#| build_type: "lib"
#| eval: false
- xsw: switch (@as(u8, 1)) {
- 1 => {
- try stdout.print("First branch\n", .{});
- continue :xsw 2;
- },
- 2 => continue :xsw 3,
- 3 => return,
- 4 => {},
- else => {
- try stdout.print("Unmatched case, value: {d}\n", .{@as(u8, 1)});
- },
- }
+xsw: switch (@as(u8, 1)) {
+ 1 => {
+ try stdout.print("First branch\n", .{});
+ continue :xsw 2;
+ },
+ 2 => continue :xsw 3,
+ 3 => return,
+ 4 => {},
+ else => {
+ try stdout.print(
+ "Unmatched case, value: {d}\n", .{@as(u8, 1)}
+ );
+ },
+}
```
From a0532a5b0c56ef04076c7e4e66e7a3e1ef3a1a36 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 24 Nov 2024 20:20:27 -0300
Subject: [PATCH 004/151] Recompile book with changes from #95
---
_freeze/Chapters/03-structs/execute-results/html.json | 8 +++++---
docs/Chapters/03-structs.html | 7 ++++++-
docs/index.html | 4 ++--
docs/search.json | 2 +-
4 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/_freeze/Chapters/03-structs/execute-results/html.json b/_freeze/Chapters/03-structs/execute-results/html.json
index 55c219e2..a9593c0d 100644
--- a/_freeze/Chapters/03-structs/execute-results/html.json
+++ b/_freeze/Chapters/03-structs/execute-results/html.json
@@ -1,9 +1,11 @@
{
- "hash": "d6fc1ed4bd224ef816cd041a3496fc12",
+ "hash": "fe9a62d3c3cabeb2802974d2becced51",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n\n# Control flow, structs, modules and types\n\nWe have discussed a lot of Zig's syntax in the last chapter,\nespecially in @sec-root-file and @sec-main-file.\nBut we still need to discuss some other very important\nelements of the language. Elements that you will use constantly on your day-to-day\nroutine.\n\nWe begin this chapter by discussing the different keywords and structures\nin Zig related to control flow (e.g. loops and if statements).\nThen, we talk about structs and how they can be used to do some\nbasic Object-Oriented (OOP) patterns in Zig. We also talk about\ntype inference and type casting.\nFinally, we end this chapter by discussing modules, and how they relate\nto structs.\n\n\n\n## Control flow {#sec-zig-control-flow}\n\nSometimes, you need to make decisions in your program. Maybe you need to decide\nwhether or not to execute a specific piece of code. Or maybe,\nyou need to apply the same operation over a sequence of values. These kinds of tasks,\ninvolve using structures that are capable of changing the \"control flow\" of our program.\n\nIn computer science, the term \"control flow\" usually refers to the order in which expressions (or commands)\nare evaluated in a given language or program. But this term is also used to refer\nto structures that are capable of changing this \"evaluation order\" of the commands\nexecuted by a given language/program.\n\nThese structures are better known\nby a set of terms, such as: loops, if/else statements, switch statements, among others. So,\nloops and if/else statements are examples of structures that can change the \"control\nflow\" of our program. The keywords `continue` and `break` are also examples of symbols\nthat can change the order of evaluation, since they can move our program to the next iteration\nof a loop, or make the loop stop completely.\n\n\n### If/else statements\n\nAn if/else statement performs a \"conditional flow operation\".\nA conditional flow control (or choice control) allows you to execute\nor ignore a certain block of commands based on a logical condition.\nMany programmers and computer science professionals also use\nthe term \"branching\" in this case.\nIn essence, an if/else statement allow us to use the result of a logical test\nto decide whether or not to execute a given block of commands.\n\nIn Zig, we write if/else statements by using the keywords `if` and `else`.\nWe start with the `if` keyword followed by a logical test inside a pair\nof parentheses, followed by a pair of curly braces which contains the lines\nof code to be executed in case the logical test returns the value `true`.\n\nAfter that, you can optionally add an `else` statement. To do that, just add the `else`\nkeyword followed by a pair of curly braces, with the lines of code\nto executed in case the logical test defined at `if` returns `false`.\n\nIn the example below, we are testing if the object `x` contains a number\nthat is greater than 10. Judging by the output printed to the console,\nwe know that this logical test returned `false`. Because the output\nin the console is compatible with the line of code present in the\n`else` branch of the if/else statement.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst x = 5;\nif (x > 10) {\n try stdout.print(\n \"x > 10!\\n\", .{}\n );\n} else {\n try stdout.print(\n \"x <= 10!\\n\", .{}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nx <= 10!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n### Switch statements {#sec-switch}\n\nSwitch statements are also available in Zig, and they have a very similar syntax to a switch statement in Rust.\nAs you would expect, to write a switch statement in Zig we use the `switch` keyword.\nWe provide the value that we want to \"switch over\" inside a\npair of parentheses. Then, we list the possible combinations (or \"branches\")\ninside a pair of curly braces.\n\nLet's take a look at the code example below. You can see that\nI'm creating an enum type called `Role`. We talk more about enums in @sec-enum.\nBut in summary, this `Role` type is listing different types of roles in a fictitious\ncompany, like `SE` for Software Engineer, `DE` for Data Engineer, `PM` for Product Manager,\netc.\n\nNotice that we are using the value from the `role` object in the\nswitch statement, to discover which exact area we need to store in the `area` variable object.\nAlso notice that we are using type inference inside the switch statement, with the dot character,\nas we are going to describe in @sec-type-inference.\nThis makes the `zig` compiler infer the correct data type of the values (`PM`, `SE`, etc.) for us.\n\nAlso notice that, we are grouping multiple values in the same branch of the switch statement.\nWe just separate each possible value with a comma. For example, if `role` contains either `DE` or `DA`,\nthe `area` variable would contain the value `\"Data & Analytics\"`, instead of `\"Platform\"` or `\"Sales\"`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst Role = enum {\n SE, DPE, DE, DA, PM, PO, KS\n};\n\npub fn main() !void {\n var area: []const u8 = undefined;\n const role = Role.SE;\n switch (role) {\n .PM, .SE, .DPE, .PO => {\n area = \"Platform\";\n },\n .DE, .DA => {\n area = \"Data & Analytics\";\n },\n .KS => {\n area = \"Sales\";\n },\n }\n try stdout.print(\"{s}\\n\", .{area});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nPlatform\n```\n\n\n:::\n:::\n\n\n\n\n\n\n#### Switch statements must exhaust all possibilities\n\nOne very important aspect about switch statements in Zig\nis that they must exhaust all existing possibilities.\nIn other words, all possible values that could be found inside the `order`\nobject must be explicitly handled in this switch statement.\n\nSince the `role` object have type `Role`, the only possible values to\nbe found inside this object are `PM`, `SE`, `DPE`, `PO`, `DE`, `DA` and `KS`.\nThere are no other possible values to be stored in this `role` object.\nThus, the switch statements must have a combination (branch) for each one of these values.\nThis is what \"exhaust all existing possibilities\" means. The switch statement covers\nevery possible case.\n\nTherefore, you cannot write a switch statement in Zig, and leave an edge case\nwith no explicit action to be taken.\nThis is a similar behaviour to switch statements in Rust, which also have to\nhandle all possible cases.\n\n\n\n#### The else branch\n\nTake a look at the `dump_hex_fallible()` function below as an example. This function\ncomes from the Zig Standard Library. More precisely, from the\n[`debug.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/debug.zig)[^debug-mod].\nThere are multiple lines in this function, but I omitted them to focus solely on the\nswitch statement found in this function. Notice that this switch statement has four\npossible cases (i.e. four explicit branches). Also, notice that we used an `else` branch\nin this case.\n\nAn `else` branch in a switch statement works as the \"default branch\".\nWhenever you have multiple cases in your switch statement where\nyou want to apply the exact same action, you can use an `else` branch to do that.\n\n[^debug-mod]: \n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub fn dump_hex_fallible(bytes: []const u8) !void {\n // Many lines ...\n switch (byte) {\n '\\n' => try writer.writeAll(\"␊\"),\n '\\r' => try writer.writeAll(\"␍\"),\n '\\t' => try writer.writeAll(\"␉\"),\n else => try writer.writeByte('.'),\n }\n}\n```\n:::\n\n\n\n\n\nMany programmers would also use an `else` branch to handle a \"not supported\" case.\nThat is, a case that cannot be properly handled by your code, or, just a case that\nshould not be \"fixed\". Therefore, you can use an `else` branch to panic (or raise an error)\nin your program to stop the current execution.\n\nTake the code example below. We can see that, we are handling the cases\nfor the `level` object being either 1, 2, or 3. All other possible cases are not supported by default,\nand, as consequence, we raise a runtime error in such cases through the `@panic()` built-in function.\n\nAlso notice that, we are assigning the result of the switch statement to a new object called `category`.\nThis is another thing that you can do with switch statements in Zig. If a branch\noutputs a value as result, you can store the result value of the switch statement into\na new object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 1, 2 => \"beginner\",\n 3 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n:::\n\n\n\n\n\n```\nthread 13103 panic: Not supported level!\nt.zig:9:13: 0x1033c58 in main (switch2)\n @panic(\"Not supported level!\");\n ^\n```\n\n\n\n#### Using ranges in switch\n\nFurthermore, you can also use ranges of values in switch statements.\nThat is, you can create a branch in your switch statement that is used\nwhenever the input value is within the specified range. These \"range expressions\"\nare created with the operator `...`. It is important\nto emphasize that the ranges created by this operator are\ninclusive on both ends.\n\nFor example, I could easily change the previous code example to support all\nlevels between 0 and 100. Like this:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 0...25 => \"beginner\",\n 26...75 => \"intermediary\",\n 76...100 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nbeginner\n```\n\n\n:::\n:::\n\n\n\n\n\nThis is neat, and it works with character ranges too. That is, I could\nsimply write `'a'...'z'`, to match any character value that is a\nlowercase letter, and it would work fine.\n\n\n#### Labeled switch statements\n\nIn @sec-blocks we have talked about labeling blocks, and also, about using these labels\nto return a value from the block. Well, from version 0.14.0 and onwards of the `zig` compiler,\nyou can also apply labels over switch statements, which makes it possible to almost implement a\n\"C `goto`\" like pattern.\n\nFor example, if you give the label `xsw` to a switch statement, you can use this\nlabel in conjunction with the `continue` keyword to go back to the beginning of the switch\nstatement. In the example below, the execution goes back to the beginning of the\nswitch statement two times, before ending at the `3` branch.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nxsw: switch (@as(u8, 1)) {\n 1 => {\n try stdout.print(\"First branch\\n\", .{});\n continue :xsw 2;\n },\n 2 => continue :xsw 3,\n 3 => return,\n 4 => {},\n}\n```\n:::\n\n\n\n\n\n\n### The `defer` keyword {#sec-defer}\n\nWith the `defer` keyword you can register an expression to be executed when you exit the current scope.\nTherefore, this keyword has a similar functionality as the `on.exit()` function from R.\nTake the `foo()` function below as an example. When we execute this `foo()` function, the expression\nthat prints the message \"Exiting function ...\" is getting executed only when the function exits\nits scope.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn foo() !void {\n defer std.debug.print(\n \"Exiting function ...\\n\", .{}\n );\n try stdout.print(\"Adding some numbers ...\\n\", .{});\n const x = 2 + 2; _ = x;\n try stdout.print(\"Multiplying ...\\n\", .{});\n const y = 2 * 8; _ = y;\n}\n\npub fn main() !void {\n try foo();\n}\n```\n:::\n\n\n\n\n\n```\nAdding some numbers ...\nMultiplying ...\nExiting function ...\n```\n\nTherefore, we can use `defer` to declare an expression that is going to be executed\nwhen your code exits the current scope. Some programmers like to interpret the phrase \"exit of the current scope\"\nas \"the end of the current scope\". But this interpretation might not be entirely correct, depending\non what you consider as \"the end of the current scope\".\n\nI mean, what do you consider as **the end** of the current scope? Is it the closing curly bracket (`}`) of the scope?\nIs it when the last expression in the function get's executed? Is it when the function returns to the previous scope?\nEtc. For example, it would not be correct to interpret the \"exit of the current scope\" as the closing\ncurly bracket of the scope. Because the function might exit from an earlier position than this\nclosing curly bracket (e.g. an error value was generated at a previous line inside the function;\nthe function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.\n\nNow, if you remember of what we have discussed in @sec-blocks, there are multiple structures in the language\nthat create their own separate scopes. For/while loops, if/else statements,\nfunctions, normal blocks, etc. This also affects the interpretation of `defer`.\nFor example, if you use `defer` inside a for loop, then, the given expression\nwill be executed everytime this specific for loop exits its own scope.\n\nBefore we continue, is worth emphasizing that the `defer` keyword is an \"unconditional defer\".\nWhich means that the given expression will be executed no matter how the code exits\nthe current scope. For example, your code might exit the current scope because of an error value\nbeing generated, or, because of a return statement, or, a break statement, etc.\n\n\n\n### The `errdefer` keyword {#sec-errdefer1}\n\nOn the previous section, we have discussed the `defer` keyword, which you can use to\nregister an expression to be executed at the exit of the current scope.\nBut this keyword has a brother, which is the `errdefer` keyword. While `defer`\nis an \"unconditional defer\", the `errdefer` keyword is a \"conditional defer\".\nWhich means that the given expression is executed only when you exit the current\nscope on a very specific circumstance.\n\nIn more details, the expression given to `errdefer` is executed only when an error occurs in the current scope.\nTherefore, if the function (or for/while loop, if/else statement, etc.) exits the current scope\nin a normal situation, without errors, the expression given to `errdefer` is not executed.\n\nThis makes the `errdefer` keyword one of the many tools available in Zig for error handling.\nIn this section, we are more concerned with the control flow aspects around `errdefer`.\nBut we are going to discuss `errdefer` later as a error handling tool in @sec-errdefer2.\n\nThe code example below demonstrates three things:\n\n- that `defer` is an \"unconditional defer\", because the given expression get's executed regardless of how the function `foo()` exits its own scope.\n- that `errdefer` is executed because the function `foo()` returned an error value.\n- that `defer` and `errdefer` expressions are executed in a LIFO (*last in, first out*) order.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n defer i = 2;\n try foo();\n}\n```\n:::\n\n\n\n\n\n```\nValue of i: 2\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\nWhen I say that \"defer expressions\" are executed in a LIFO order, what I want to say is that\nthe last `defer` or `errdefer` expressions in the code are the first ones to be executed.\nYou could also interpret this as: \"defer expressions\" are executed from bottom to top, or,\nfrom last to first.\n\nTherefore, if I change the order of the `defer` and `errdefer` expressions, you will notice that\nthe value of `i` that get's printed to the console changes to 1. This doesn't mean that the\n`defer` expression was not executed in this case. This actually means that the `defer` expression\nwas executed only after the `errdefer` expression. The code example below demonstrates this:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n defer i = 2;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n try foo();\n}\n```\n:::\n\n\n\n\n\n```\nValue of i: 1\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\n\n\n### For loops\n\nA loop allows you to execute the same lines of code multiple times,\nthus, creating a \"repetition space\" in the execution flow of your program.\nLoops are particularly useful when we want to replicate the same function\n(or the same set of commands) over different inputs.\n\nThere are different types of loops available in Zig. But the most\nessential of them all is probably the *for loop*. A for loop is\nused to apply the same piece of code over the elements of a slice, or, an array.\n\nFor loops in Zig use a syntax that may be unfamiliar to programmers coming from\nother languages. You start with the `for` keyword, then, you\nlist the items that you want to iterate\nover inside a pair of parentheses. Then, inside of a pair of pipes (`|`)\nyou should declare an identifier that will serve as your iterator, or,\nthe \"repetition index of the loop\".\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (items) |value| {\n // code to execute\n}\n```\n:::\n\n\n\n\n\nTherefore, instead of using a `(value in items)` syntax,\nin Zig, for loops use the syntax `(items) |value|`. In the example\nbelow, you can see that we are looping through the items\nof the array stored at the object `name`, and printing to the\nconsole the decimal representation of each character in this array.\n\nIf we wanted, we could also iterate through a slice (or a portion) of\nthe array, instead of iterating through the entire array stored in the `name` object.\nJust use a range selector to select the section you want. For example,\nI could provide the expression `name[0..3]` to the for loop, to iterate\njust through the first 3 elements in the array.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name = [_]u8{'P','e','d','r','o'};\nfor (name) |char| {\n try stdout.print(\"{d} | \", .{char});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n80 | 101 | 100 | 114 | 111 | \n```\n\n\n:::\n:::\n\n\n\n\n\nIn the above example we are using the value itself of each\nelement in the array as our iterator. But there are many situations where\nwe need to use an index instead of the actual values of the items.\n\nYou can do that by providing a second set of items to iterate over.\nMore precisely, you provide the range selector `0..` to the for loop. So,\nyes, you can use two different iterators at the same time in a for\nloop in Zig.\n\nBut remember from @sec-assignments that, every object\nyou create in Zig must be used in some way. So if you declare two iterators\nin your for loop, you must use both iterators inside the for loop body.\nBut if you want to use just the index iterator, and not use the \"value iterator\",\nthen, you can discard the value iterator by maching the\nvalue items to the underscore character, like in the example below:\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (name, 0..) |_, i| {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n:::\n\n\n\n\n\n```\n0 | 1 | 2 | 3 | 4 |\n```\n\n\n### While loops\n\nA while loop is created from the `while` keyword. A `for` loop\niterates through the items of an array, but a `while` loop\nwill loop continuously, and infinitely, until a logical test\n(specified by you) becomes false.\n\nYou start with the `while` keyword, then, you define a logical\nexpression inside a pair of parentheses, and the body of the\nloop is provided inside a pair of curly braces, like in the example below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) {\n try stdout.print(\"{d} | \", .{i});\n i += 1;\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\n\nYou can also specify the increment expression to be used at the beginning of a while loop.\nTo do that, we write the increment expression inside a pair of parentheses after a colon character (`:`).\nThe code example below demonstrates this other pattern.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) : (i += 1) {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\n\n### Using `break` and `continue`\n\nIn Zig, you can explicitly stop the execution of a loop, or, jump to the next iteration of the loop, by using\nthe keywords `break` and `continue`, respectively. The `while` loop presented in the next code example is,\nat first sight, an infinite loop. Because the logical value inside the parenthese will always be equal to `true`.\nBut what makes this `while` loop stop when the `i` object reaches the count\n10? It is the `break` keyword!\n\nInside the while loop, we have an if statement that is constantly checking if the `i` variable\nis equal to 10. Since we are incrementing the value of `i` at each iteration of the\nwhile loop, this `i` object will eventually be equal to 10, and when it is, the if statement\nwill execute the `break` expression, and, as a result, the execution of the while loop is stopped.\n\nNotice the use of the `expect()` function from the Zig Standard Library after the while loop.\nThis `expect()` function is an \"assert\" type of function.\nThis function checks if the logical test provided is equal to true. If so, the function do nothing.\nOtherwise (i.e. the logical test is equal to false), the function raises an assertion error.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: usize = 0;\nwhile (true) {\n if (i == 10) {\n break;\n }\n i += 1;\n}\ntry std.testing.expect(i == 10);\ntry stdout.print(\"Everything worked!\", .{});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nEverything worked!\n```\n\n\n:::\n:::\n\n\n\n\n\nSince this code example was executed successfully by the `zig` compiler,\nwithout raising any errors, we known that, after the execution of the while loop,\nthe `i` object is equal to 10. Because if it wasn't equal to 10, an error would have\nbeen raised by `expect()`.\n\nNow, in the next example, we have a use case for\nthe `continue` keyword. The if statement is constantly\nchecking if the current index is a multiple of 2. If\nit is, we jump to the next iteration of the loop.\nOtherwise, the loop just prints the current index to the console.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [_]u8{1,2,3,4,5,6};\nfor (ns) |i| {\n if ((i % 2) == 0) {\n continue;\n }\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 3 | 5 | \n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n## Function parameters are immutable {#sec-fun-pars}\n\nWe have already discussed a lot of the syntax behind function declarations in @sec-root-file and @sec-main-file.\nBut I want to emphasize a curious fact about function parameters (a.k.a. function arguments) in Zig.\nIn summary, function parameters are immutable in Zig.\n\nTake the code example below, where we declare a simple function that just tries to add\nsome amount to the input integer, and returns the result back. If you look closely\nat the body of this `add2()` function, you will notice that we try\nto save the result back into the `x` function argument.\n\nIn other words, this function not only uses the value that it received through the function argument\n`x`, but it also tries to change the value of this function argument, by assigning the addition result\ninto `x`. However, function arguments in Zig are immutable. You cannot change their values, or, you\ncannot assign values to them inside the body's function.\n\nThis is the reason why, the code example below does not compile successfully. If you try to compile\nthis code example, you will get a compile error message about \"trying to change the value of a\nimmutable (i.e. constant) object\".\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: u32) u32 {\n x = x + 2;\n return x;\n}\n\npub fn main() !void {\n const y = add2(4);\n std.debug.print(\"{d}\\n\", .{y});\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:3:5: error: cannot assign to constant\n x = x + 2;\n ^\n```\n\n\n### A free optimization\n\nIf a function argument receives as input an object whose data type is\nany of the primitive types that we have listed in @sec-primitive-data-types,\nthis object is always passed by value to the function. In other words, this object\nis copied into the function stack frame.\n\nHowever, if the input object have a more complex data type, for example, it might\nbe a struct instance, or an array, or an union value, etc., in cases like that, the `zig` compiler\nwill take the liberty of deciding for you which strategy is best. Thus, the `zig` compiler will\npass your object to the function either by value, or by reference. The compiler will always\nchoose the strategy that is faster for you.\nThis optimization that you get for free is possible only because function arguments are\nimmutable in Zig.\n\n\n### How to overcome this barrier\n\nThere are some situations where you might need to change the value of your function argument\ndirectly inside the function's body. This happens more often when we are passing\nC structs as inputs to Zig functions.\n\nIn a situation like this, you can overcome this barrier by using a pointer. In other words,\ninstead of passing a value as input to the argument, you can pass a \"pointer to value\" instead.\nYou can change the value that the pointer points to, by dereferencing it.\n\nTherefore, if we take our previous `add2()` example, we can change the value of the\nfunction argument `x` inside the function's body by marking the `x` argument as a\n\"pointer to a `u32` value\" (i.e. `*u32` data type), instead of a `u32` value.\nBy making it a pointer, we can finally alter the value of this function argument directly inside\nthe body of the `add2()` function. You can see that the code example below compiles successfully.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: *u32) void {\n const d: u32 = 2;\n x.* = x.* + d;\n}\n\npub fn main() !void {\n var x: u32 = 4;\n add2(&x);\n std.debug.print(\"Result: {d}\\n\", .{x});\n}\n```\n:::\n\n\n\n\n\n```\nResult: 6\n```\n\n\nEven in this code example above, the `x` argument is still immutable. Which means that the pointer itself is immutable.\nTherefore, you cannot change the memory address that it points to. However, you can dereference the pointer\nto access the value that it points to, and also, to change this value, if you need to.\n\n\n\n\n\n## Structs and OOP {#sec-structs-and-oop}\n\nZig is a language more closely related to C (which is a procedural language),\nthan it is to C++ or Java (which are object-oriented languages). Because of that, you do not\nhave advanced OOP (Object-Oriented Programming) patterns available in Zig, such as classes, interfaces or\nclass inheritance. Nonetheless, OOP in Zig is still possible by using struct definitions.\n\nWith struct definitions, you can create (or define) a new data type in Zig. These struct definitions work the same way as they work in C.\nYou give a name to this new struct (or, to this new data type you are creating), then, you list the data members of this new struct. You can\nalso register functions inside this struct, and they become the methods of this particular struct (or data type), so that, every object\nthat you create with this new type, will always have these methods available and associated with them.\n\nIn C++, when we create a new class, we normally have a constructor method (or, a constructor function) which\nis used to construct (or, to instantiate) every object of this particular class, and we also have\na destructor method (or a destructor function), which is the function responsible for destroying\nevery object of this class.\n\nIn Zig, we normally declare the constructor and the destructor methods\nof our structs, by declaring an `init()` and a `deinit()` methods inside the struct.\nThis is just a naming convention that you will find across the entire Zig Standard Library.\nSo, in Zig, the `init()` method of a struct is normally the constructor method of the class represented by this struct.\nWhile the `deinit()` method is the method used for destroying an existing instance of that struct.\n\nThe `init()` and `deinit()` methods are both used extensively in Zig code, and you will see both of\nthem being used when we talk about allocators in @sec-allocators.\nBut, as another example, let's build a simple `User` struct to represent a user of some sort of system.\n\nIf you look at the `User` struct below, you can see the `struct` keyword.\nNotice the data members of this struct: `id`, `name` and `email`. Every data member has its\ntype explicitly annotated, with the colon character (`:`) syntax that we described earlier in @sec-root-file.\nBut also notice that every line in the struct body that describes a data member, ends with a comma character (`,`).\nSo every time you declare a data member in your Zig code, always end the line with a comma character, instead\nof ending it with the traditional semicolon character (`;`).\n\nNext, we have registered an `init()` function as a method\nof this `User` struct. This `init()` method is the constructor method that we will use to instantiate\nevery new `User` object. That is why this `init()` function returns a new `User` object as result.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst User = struct {\n id: u64,\n name: []const u8,\n email: []const u8,\n\n pub fn init(id: u64,\n name: []const u8,\n email: []const u8) User {\n\n return User {\n .id = id,\n .name = name,\n .email = email\n };\n }\n\n pub fn print_name(self: User) !void {\n try stdout.print(\"{s}\\n\", .{self.name});\n }\n};\n\npub fn main() !void {\n const u = User.init(1, \"pedro\", \"email@gmail.com\");\n try u.print_name();\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\npedro\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `pub` keyword plays an important role in struct declarations, and OOP in Zig.\nEvery method that you declare in your struct that is marked with the keyword `pub`,\nbecomes a public method of this particular struct.\n\nSo every method that you create inside your struct, is, at first, a private method\nof that struct. Meaning that, this method can only be called from within this\nstruct. But, if you mark this method as public, with the keyword `pub`, then,\nyou can call the method directly from an instance of the `User` struct.\n\nIn other words, the functions marked by the keyword `pub`\nare members of the public API of that struct.\nFor example, if I did not mark the `print_name()` method as public,\nthen, I could not execute the line `u.print_name()`. Because I would\nnot be authorized to call this method directly in my code.\n\n\n\n### Anonymous struct literals {#sec-anonymous-struct-literals}\n\nYou can declare a struct object as a literal value. When we do that, we normally specify the\ndata type of this struct literal by writing its data type just before the opening curly brace.\nFor example, I could write a struct literal value of the type `User` that we have defined\nin the previous section like this:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst eu = User {\n .id = 1,\n .name = \"Pedro\",\n .email = \"someemail@gmail.com\"\n};\n_ = eu;\n```\n:::\n\n\n\n\n\nHowever, in Zig, we can also write an anonymous struct literal. That is, you can write a\nstruct literal, but not specify explicitly the type of this particular struct.\nAn anonymous struct is written by using the syntax `.{}`. So, we essentially\nreplaced the explicit type of the struct literal with a dot character (`.`).\n\nAs we described in @sec-type-inference, when you put a dot before a struct literal,\nthe type of this struct literal is automatically inferred by the `zig` compiler.\nIn essence, the `zig` compiler will look for some hint of what is the type of that struct.\nThis hint can be the type annotation of a function argument,\nor the return type annotation of the function that you are using, or the type annotation\nof an existing object.\nIf the compiler does find such type annotation, it will use this\ntype in your literal struct.\n\nAnonymous structs are very commonly used as inputs to function arguments in Zig.\nOne example that you have seen already constantly, is the `print()`\nfunction from the `stdout` object.\nThis function takes two arguments.\nThe first argument, is a template string, which should\ncontain string format specifiers in it, which tells how the values provided\nin the second argument should be printed into the message.\n\nWhile the second argument is a struct literal that lists the values\nto be printed into the template message specified in the first argument.\nYou normally want to use an anonymous struct literal here, so that the\n`zig` compiler do the job of specifying the type of this particular\nanonymous struct for you.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHello, world!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n### Struct declarations must be constant\n\nTypes in Zig must be `const` or `comptime` (we are going to talk more about comptime in @sec-comptime).\nWhat this means is that you cannot create a new data type, and mark it as variable with the `var` keyword.\nSo struct declarations are always constant. You cannot declare a new struct type using the `var` keyword.\nIt must be `const`.\n\nIn the `Vec3` example below, this declaration is allowed because I'm using the `const` keyword\nto declare this new data type.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n};\n```\n:::\n\n\n\n\n\n\n### The `self` method argument {#sec-self-arg}\n\nIn every language that have OOP, when we declare a method of some class or struct, we\nusually declare this method as a function that has a `self` argument.\nThis `self` argument is the reference to the object itself from which the method\nis being called from.\n\nIt is not mandatory to use this `self` argument. But why would you not use this `self` argument?\nThere is no reason to not use it. Because the only way to get access to the data stored in the\ndata members of your struct is to access them through this `self` argument.\nIf you don't need to use the data in the data members of your struct inside your method, you very likely don't need\na method. You can just declare this logic as a simple function, outside of your\nstruct declaration.\n\n\nTake the `Vec3` struct below. Inside this `Vec3` struct we declared a method named `distance()`.\nThis method calculates the distance between two `Vec3` objects, by following the distance\nformula in euclidean space. Notice that this `distance()` method takes two `Vec3` objects\nas input, `self` and `other`.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n};\n```\n:::\n\n\n\n\n\n\nThe `self` argument corresponds to the `Vec3` object from which this `distance()` method\nis being called from. While the `other` is a separate `Vec3` object that is given as input\nto this method. In the example below, the `self` argument corresponds to the object\n`v1`, because the `distance()` method is being called from the `v1` object,\nwhile the `other` argument corresponds to the object `v2`.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst v1 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nconst v2 = Vec3 {\n .x = 5.1, .y = 5.6, .z = 1.6\n};\n\nstd.debug.print(\n \"Distance: {d}\\n\",\n .{v1.distance(v2)}\n);\n```\n:::\n\n\n\n\n\n```\nDistance: 3.3970575502926055\n```\n\n\n\n### About the struct state\n\nSometimes you don't need to care about the state of your struct object. Sometimes, you just need\nto instantiate and use the objects, without altering their state. You can notice that when you have methods\ninside your struct declaration that might use the values that are present in the data members, but they\ndo not alter the values in these data members of the struct in anyway.\n\nThe `Vec3` struct that was presented in @sec-self-arg is an example of that.\nThis struct have a single method named `distance()`, and this method does use the values\npresent in all three data members of the struct (`x`, `y` and `z`). But at the same time,\nthis method does not change the values of these data members at any point.\n\nAs a result of that, when we create `Vec3` objects we usually create them as\nconstant objects, like the `v1` and `v2` objects presented in @sec-self-arg.\nWe can create them as variable objects with the `var` keyword,\nif we want to. But because the methods of this `Vec3` struct do not change\nthe state of the objects in any point, it's unnecessary to mark them\nas variable objects.\n\nBut why? Why am I talking about this here? It's because the `self` argument\nin the methods is affected depending on whether the\nmethods present in a struct change or don't change the state of the object itself.\nMore specifically, when you have a method in a struct that changes the state\nof the object (i.e. change the value of a data member), the `self` argument\nin this method must be annotated in a different manner.\n\nAs I described in @sec-self-arg, the `self` argument in methods of\na struct is the argument that receives as input the object from which the method\nwas called from. We usually annotate this argument in the methods by writing `self`,\nfollowed by the colon character (`:`), and the data type of the struct to which\nthe method belongs to (e.g. `User`, `Vec3`, etc.).\n\nIf we take the `Vec3` struct that we defined in the previous section as an example,\nwe can see in the `distance()` method that this `self` argument is annotated as\n`self: Vec3`. Because the state of the `Vec3` object is never altered by this\nmethod.\n\nBut what if we do have a method that alters the state of the object, by altering the\nvalues of its data members, how should we annotate `self` in this instance? The answer is:\n\"we should annotate `self` as a pointer of `x`, instead of just `x`\".\nIn other words, you should annotate `self` as `self: *x`, instead of annotating it\nas `self: x`.\n\nIf we create a new method inside the `Vec3` object that, for example, expands the\nvector by multiplying its coordinates by a factor of two, then, we need to follow\nthis rule specified in the previous paragraph. The code example below demonstrates\nthis idea:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n\n pub fn twice(self: *Vec3) void {\n self.x = self.x * 2.0;\n self.y = self.y * 2.0;\n self.z = self.z * 2.0;\n }\n};\n```\n:::\n\n\n\n\n\nNotice in the code example above that we have added a new method\nto our `Vec3` struct named `twice()`. This method doubles the\ncoordinate values of our vector object. In the\ncase of the `twice()` method, we annotated the `self` argument as `*Vec3`,\nindicating that this argument receives a pointer (or a reference, if you prefer to call it this way)\nto a `Vec3` object as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar v3 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nv3.twice();\nstd.debug.print(\"Doubled: {d}\\n\", .{v3.x});\n```\n:::\n\n\n\n\n\n```\nDoubled: 8.4\n```\n\n\n\nNow, if you change the `self` argument in this `twice()` method to `self: Vec3`, like in the\n`distance()` method, you will get the compiler error exposed below as result. Notice that this\nerror message is showing a line from the `twice()` method body,\nindicating that you cannot alter the value of the `x` data member.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// If we change the function signature of double to:\n pub fn twice(self: Vec3) void {\n```\n:::\n\n\n\n\n\n```\nt.zig:16:13: error: cannot assign to constant\n self.x = self.x * 2.0;\n ~~~~^~\n```\n\nThis error message indicates that the `x` data member belongs to a constant object,\nand, because of that, it cannot be changed. Ultimately, this error message\nis telling us that the `self` argument is constant.\n\nIf you take some time, and think hard about this error message, you will understand it.\nYou already have the tools to understand why we are getting this error message.\nWe have talked about it already in @sec-fun-pars.\nSo remember, every function argument is immutable in Zig, and `self`\nis no exception to this rule.\n\nIn this example, we marked the `v3` object as a variable object.\nBut this does not matter. Because it is not about the input object, it is about\nthe function argument.\n\nThe problem begins when we try to alter the value of `self` directly, which is a function argument,\nand, every function argument is immutable by default. You may ask yourself how can we overcome\nthis barrier, and once again, the solution was also discussed in @sec-fun-pars.\nWe overcome this barrier, by explicitly marking the `self` argument as a pointer.\n\n\n::: {.callout-note}\nIf a method of your `x` struct alters the state of the object, by\nchanging the value of any data member, then, remember to use `self: *x`,\ninstead of `self: x` in the function signature of this method.\n:::\n\n\nYou could also interpret the content discussed in this section as:\n\"if you need to alter the state of your `x` struct object in one of its methods,\nyou must explicitly pass the `x` struct object by reference to the `self` argument of this method\".\n\n\n\n## Type inference {#sec-type-inference}\n\nZig is a strongly typed language. But, there are some situations\nwhere you don't have to explicitly write the type of every single object in your source code,\nas you would expect from a traditional strongly typed language, such as C and C++.\n\nIn some situations, the `zig` compiler can use type inference to solve the data types for you, easing some of\nthe burden that you carry as a developer.\nThe most common way this happens is through function arguments that receive struct objects\nas input.\n\nIn general, type inference in Zig is done by using the dot character (`.`).\nEverytime you see a dot character written before a struct literal, or before an enum value, or something like that,\nyou know that this dot character is playing a special party in this place. More specifically, it is\ntelling the `zig` compiler something along the lines of: \"Hey! Can you infer the type of this\nvalue for me? Please!\". In other words, this dot character is playing a similar role as the `auto` keyword in C++.\n\nI gave you some examples of this in @sec-anonymous-struct-literals, where we used anonymous struct literals.\nAnonymous struct literals are, struct literals that use type inference to\ninfer the exact type of this particular struct literal.\nThis type inference is done by looking for some minimal hint of the correct data type to be used.\nYou could say that the `zig` compiler looks for any neighbouring type annotation that might tell it\nwhat the correct type would be.\n\nAnother common place where we use type inference in Zig is at switch statements (which we talked about in @sec-switch).\nI also gave some other examples of type inference in @sec-switch, where we were inferring the data types of enum values listed inside\nof switch statements (e.g. `.DE`).\nBut as another example, take a look at this `fence()` function reproduced below,\nwhich comes from the [`atomic.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/atomic.zig)[^fence-fn]\nof the Zig Standard Library.\n\n[^fence-fn]: .\n\nThere are a lot of things in this function that we haven't talked about yet, such as:\nwhat `comptime` means? `inline`? `extern`?\nLet's just ignore all of these things, and focus solely on the switch statement\nthat is inside this function.\n\nWe can see that this switch statement uses the `order` object as input. This `order`\nobject is one of the inputs of this `fence()` function, and we can see in the type annotation,\nthat this object is of type `AtomicOrder`. We can also see a bunch of values inside the\nswitch statements that begin with a dot character, such as `.release` and `.acquire`.\n\nBecause these weird values contain a dot character before them, we are asking the `zig`\ncompiler to infer the types of these values inside the switch statement. Then, the `zig`\ncompiler is looking into the current context where these values are being used, and it is\ntrying to infer the types of these values.\n\nSince they are being used inside a switch statement, the `zig` compiler looks into the type\nof the input object given to the switch statement, which is the `order` object in this case.\nBecause this object have type `AtomicOrder`, the `zig` compiler infers that these values\nare data members from this type `AtomicOrder`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub inline fn fence(self: *Self, comptime order: AtomicOrder) void {\n // many lines of code ...\n if (builtin.sanitize_thread) {\n const tsan = struct {\n extern \"c\" fn __tsan_acquire(addr: *anyopaque) void;\n extern \"c\" fn __tsan_release(addr: *anyopaque) void;\n };\n\n const addr: *anyopaque = self;\n return switch (order) {\n .unordered, .monotonic => @compileError(\n @tagName(order)\n ++ \" only applies to atomic loads and stores\"\n ),\n .acquire => tsan.__tsan_acquire(addr),\n .release => tsan.__tsan_release(addr),\n .acq_rel, .seq_cst => {\n tsan.__tsan_acquire(addr);\n tsan.__tsan_release(addr);\n },\n };\n }\n\n return @fence(order);\n}\n```\n:::\n\n\n\n\n\nThis is how basic type inference is done in Zig. If we didn't use the dot character before\nthe values inside this switch statement, then, we would be forced to explicitly write\nthe data types of these values. For example, instead of writing `.release` we would have to\nwrite `AtomicOrder.release`. We would have to do this for every single value\nin this switch statement, and this is a lot of work. That is why type inference\nis commonly used on switch statements in Zig.\n\n\n\n## Type casting {#sec-type-cast}\n\nIn this section, I want to discuss type casting (or, type conversion) with you.\nWe use type casting when we have an object of type \"x\", and we want to convert\nit into an object of type \"y\", i.e. we want to change the data type of the object.\n\nMost languages have a formal way to perform type casting. In Rust for example, we normally\nuse the keyword `as`, and in C, we normally use the type casting syntax, e.g. `(int) x`.\nIn Zig, we use the `@as()` built-in function to cast an object of type \"x\", into\nan object of type \"y\".\n\nThis `@as()` function is the preferred way to perform type conversion (or type casting)\nin Zig. Because it is explicit, and, it also performs the casting only if it\nis unambiguous and safe. To use this function, you just provide the target data type\nin the first argument, and, the object that you want cast as the second argument.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 500;\n const y = @as(u32, x);\n try expect(@TypeOf(y) == u32);\n}\n```\n:::\n\n\n\n\n\nThis is the general way to perform type casting in Zig. But remember, `@as()` works only when casting\nis unambiguous and safe, and there are situations where these assumptions do not hold. For example,\nwhen casting an integer value into a float value, or vice-versa, it is not clear to the compiler\nhow to perform this conversion safely.\n\nTherefore, we need to use specialized \"casting functions\" in such situations.\nFor example, if you want to cast an integer value into a float value, then, you\nshould use the `@floatFromInt()` function. In the inverse scenario, you should use\nthe `@intFromFloat()` function.\n\nIn these functions, you just provide the object that you want to\ncast as input. Then, the target data type of the \"type casting operation\" is determined by\nthe type annotation of the object where you are saving the results.\nIn the example below, we are casting the object `x` into a value of type `f32`,\nbecause the object `y`, which is where we are saving the results, is annotated\nas an object of type `f32`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 565;\n const y: f32 = @floatFromInt(x);\n try expect(@TypeOf(y) == f32);\n}\n```\n:::\n\n\n\n\n\nAnother built-in function that is very useful when performing type casting operations is `@ptrCast()`.\nIn essence, we use the `@as()` built-in function when we want to explicit convert (or cast) a Zig value/object\nfrom a type \"x\" to a type \"y\", etc. However, pointers (we are going to discuss pointers\nin more depth in @sec-pointer) are a special type of object in Zig,\ni.e. they are treated differently from \"normal objects\".\n\nEverytime a pointer is involved in some \"type casting operation\" in Zig, the `@ptrCast()` function is used.\nThis function works similarly to `@floatFromInt()`.\nYou just provide the pointer object that you want to cast as input to this function, and the\ntarget data type is, once again, determined by the type annotation of the object where the results are being\nstored.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const bytes align(@alignOf(u32)) = [_]u8{\n 0x12, 0x12, 0x12, 0x12\n };\n const u32_ptr: *const u32 = @ptrCast(&bytes);\n try expect(@TypeOf(u32_ptr) == *const u32);\n}\n```\n:::\n\n\n\n\n\n\n\n\n\n## Modules\n\nWe already talked about what modules are, and also, how to import other modules into\nyour current module via *import statements*. Every Zig module (i.e. a `.zig` file) that you write in your project\nis internally stored as a struct object. Take the line exposed below as an example. In this line we are importing the\nZig Standard Library into our current module.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n```\n:::\n\n\n\n\n\nWhen we want to access the functions and objects from the standard library, we\nare basically accessing the data members of the struct stored in the `std`\nobject. That is why we use the same syntax that we use in normal structs, with the dot operator (`.`)\nto access the data members and methods of the struct.\n\nWhen this \"import statement\" get's executed, the result of this expression is a struct\nobject that contains the Zig Standard Library modules, global variables, functions, etc.\nAnd this struct object get's saved (or stored) inside the constant object named `std`.\n\n\nTake the [`thread_pool.zig` module from the project `zap`](https://github.com/kprotty/zap/blob/blog/src/thread_pool.zig)[^thread]\nas an example. This module is written as if it was\na big struct. That is why we have a top-level and public `init()` method\nwritten in this module. The idea is that all top-level functions written in this\nmodule are methods from the struct, and all top-level objects and struct declarations\nare data members of this struct. The module is the struct itself.\n\n[^thread]: \n\n\nSo you would import and use this module by doing something like this:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst ThreadPool = @import(\"thread_pool.zig\");\nconst num_cpus = std.Thread.getCpuCount()\n catch @panic(\"failed to get cpu core count\");\nconst num_threads = std.math.cast(u16, num_cpus)\n catch std.math.maxInt(u16);\nconst pool = ThreadPool.init(\n .{ .max_threads = num_threads }\n);\n```\n:::\n",
- "supporting": [],
+ "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n# Control flow, structs, modules and types\n\nWe have discussed a lot of Zig's syntax in the last chapter,\nespecially in @sec-root-file and @sec-main-file.\nBut we still need to discuss some other very important\nelements of the language. Elements that you will use constantly on your day-to-day\nroutine.\n\nWe begin this chapter by discussing the different keywords and structures\nin Zig related to control flow (e.g. loops and if statements).\nThen, we talk about structs and how they can be used to do some\nbasic Object-Oriented (OOP) patterns in Zig. We also talk about\ntype inference and type casting.\nFinally, we end this chapter by discussing modules, and how they relate\nto structs.\n\n\n\n## Control flow {#sec-zig-control-flow}\n\nSometimes, you need to make decisions in your program. Maybe you need to decide\nwhether or not to execute a specific piece of code. Or maybe,\nyou need to apply the same operation over a sequence of values. These kinds of tasks,\ninvolve using structures that are capable of changing the \"control flow\" of our program.\n\nIn computer science, the term \"control flow\" usually refers to the order in which expressions (or commands)\nare evaluated in a given language or program. But this term is also used to refer\nto structures that are capable of changing this \"evaluation order\" of the commands\nexecuted by a given language/program.\n\nThese structures are better known\nby a set of terms, such as: loops, if/else statements, switch statements, among others. So,\nloops and if/else statements are examples of structures that can change the \"control\nflow\" of our program. The keywords `continue` and `break` are also examples of symbols\nthat can change the order of evaluation, since they can move our program to the next iteration\nof a loop, or make the loop stop completely.\n\n\n### If/else statements\n\nAn if/else statement performs a \"conditional flow operation\".\nA conditional flow control (or choice control) allows you to execute\nor ignore a certain block of commands based on a logical condition.\nMany programmers and computer science professionals also use\nthe term \"branching\" in this case.\nIn essence, an if/else statement allow us to use the result of a logical test\nto decide whether or not to execute a given block of commands.\n\nIn Zig, we write if/else statements by using the keywords `if` and `else`.\nWe start with the `if` keyword followed by a logical test inside a pair\nof parentheses, followed by a pair of curly braces which contains the lines\nof code to be executed in case the logical test returns the value `true`.\n\nAfter that, you can optionally add an `else` statement. To do that, just add the `else`\nkeyword followed by a pair of curly braces, with the lines of code\nto executed in case the logical test defined at `if` returns `false`.\n\nIn the example below, we are testing if the object `x` contains a number\nthat is greater than 10. Judging by the output printed to the console,\nwe know that this logical test returned `false`. Because the output\nin the console is compatible with the line of code present in the\n`else` branch of the if/else statement.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst x = 5;\nif (x > 10) {\n try stdout.print(\n \"x > 10!\\n\", .{}\n );\n} else {\n try stdout.print(\n \"x <= 10!\\n\", .{}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nx <= 10!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Switch statements {#sec-switch}\n\nSwitch statements are also available in Zig, and they have a very similar syntax to a switch statement in Rust.\nAs you would expect, to write a switch statement in Zig we use the `switch` keyword.\nWe provide the value that we want to \"switch over\" inside a\npair of parentheses. Then, we list the possible combinations (or \"branches\")\ninside a pair of curly braces.\n\nLet's take a look at the code example below. You can see that\nI'm creating an enum type called `Role`. We talk more about enums in @sec-enum.\nBut in summary, this `Role` type is listing different types of roles in a fictitious\ncompany, like `SE` for Software Engineer, `DE` for Data Engineer, `PM` for Product Manager,\netc.\n\nNotice that we are using the value from the `role` object in the\nswitch statement, to discover which exact area we need to store in the `area` variable object.\nAlso notice that we are using type inference inside the switch statement, with the dot character,\nas we are going to describe in @sec-type-inference.\nThis makes the `zig` compiler infer the correct data type of the values (`PM`, `SE`, etc.) for us.\n\nAlso notice that, we are grouping multiple values in the same branch of the switch statement.\nWe just separate each possible value with a comma. For example, if `role` contains either `DE` or `DA`,\nthe `area` variable would contain the value `\"Data & Analytics\"`, instead of `\"Platform\"` or `\"Sales\"`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst Role = enum {\n SE, DPE, DE, DA, PM, PO, KS\n};\n\npub fn main() !void {\n var area: []const u8 = undefined;\n const role = Role.SE;\n switch (role) {\n .PM, .SE, .DPE, .PO => {\n area = \"Platform\";\n },\n .DE, .DA => {\n area = \"Data & Analytics\";\n },\n .KS => {\n area = \"Sales\";\n },\n }\n try stdout.print(\"{s}\\n\", .{area});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nPlatform\n```\n\n\n:::\n:::\n\n\n\n\n\n#### Switch statements must exhaust all possibilities\n\nOne very important aspect about switch statements in Zig\nis that they must exhaust all existing possibilities.\nIn other words, all possible values that could be found inside the `order`\nobject must be explicitly handled in this switch statement.\n\nSince the `role` object have type `Role`, the only possible values to\nbe found inside this object are `PM`, `SE`, `DPE`, `PO`, `DE`, `DA` and `KS`.\nThere are no other possible values to be stored in this `role` object.\nThus, the switch statements must have a combination (branch) for each one of these values.\nThis is what \"exhaust all existing possibilities\" means. The switch statement covers\nevery possible case.\n\nTherefore, you cannot write a switch statement in Zig, and leave an edge case\nwith no explicit action to be taken.\nThis is a similar behaviour to switch statements in Rust, which also have to\nhandle all possible cases.\n\n\n\n#### The else branch\n\nTake a look at the `dump_hex_fallible()` function below as an example. This function\ncomes from the Zig Standard Library. More precisely, from the\n[`debug.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/debug.zig)[^debug-mod].\nThere are multiple lines in this function, but I omitted them to focus solely on the\nswitch statement found in this function. Notice that this switch statement has four\npossible cases (i.e. four explicit branches). Also, notice that we used an `else` branch\nin this case.\n\nAn `else` branch in a switch statement works as the \"default branch\".\nWhenever you have multiple cases in your switch statement where\nyou want to apply the exact same action, you can use an `else` branch to do that.\n\n[^debug-mod]: \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub fn dump_hex_fallible(bytes: []const u8) !void {\n // Many lines ...\n switch (byte) {\n '\\n' => try writer.writeAll(\"␊\"),\n '\\r' => try writer.writeAll(\"␍\"),\n '\\t' => try writer.writeAll(\"␉\"),\n else => try writer.writeByte('.'),\n }\n}\n```\n:::\n\n\n\n\nMany programmers would also use an `else` branch to handle a \"not supported\" case.\nThat is, a case that cannot be properly handled by your code, or, just a case that\nshould not be \"fixed\". Therefore, you can use an `else` branch to panic (or raise an error)\nin your program to stop the current execution.\n\nTake the code example below. We can see that, we are handling the cases\nfor the `level` object being either 1, 2, or 3. All other possible cases are not supported by default,\nand, as consequence, we raise a runtime error in such cases through the `@panic()` built-in function.\n\nAlso notice that, we are assigning the result of the switch statement to a new object called `category`.\nThis is another thing that you can do with switch statements in Zig. If a branch\noutputs a value as result, you can store the result value of the switch statement into\na new object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 1, 2 => \"beginner\",\n 3 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n:::\n\n\n\n\n```\nthread 13103 panic: Not supported level!\nt.zig:9:13: 0x1033c58 in main (switch2)\n @panic(\"Not supported level!\");\n ^\n```\n\n\n\n#### Using ranges in switch\n\nFurthermore, you can also use ranges of values in switch statements.\nThat is, you can create a branch in your switch statement that is used\nwhenever the input value is within the specified range. These \"range expressions\"\nare created with the operator `...`. It is important\nto emphasize that the ranges created by this operator are\ninclusive on both ends.\n\nFor example, I could easily change the previous code example to support all\nlevels between 0 and 100. Like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 0...25 => \"beginner\",\n 26...75 => \"intermediary\",\n 76...100 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nbeginner\n```\n\n\n:::\n:::\n\n\n\n\nThis is neat, and it works with character ranges too. That is, I could\nsimply write `'a'...'z'`, to match any character value that is a\nlowercase letter, and it would work fine.\n\n\n#### Labeled switch statements\n\nIn @sec-blocks we have talked about labeling blocks, and also, about using these labels\nto return a value from the block. Well, from version 0.14.0 and onwards of the `zig` compiler,\nyou can also apply labels over switch statements, which makes it possible to almost implement a\n\"C `goto`\" like pattern.\n\nFor example, if you give the label `xsw` to a switch statement, you can use this\nlabel in conjunction with the `continue` keyword to go back to the beginning of the switch\nstatement. In the example below, the execution goes back to the beginning of the\nswitch statement two times, before ending at the `3` branch.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nxsw: switch (@as(u8, 1)) {\n 1 => {\n try stdout.print(\"First branch\\n\", .{});\n continue :xsw 2;\n },\n 2 => continue :xsw 3,\n 3 => return,\n 4 => {},\n else => {\n try stdout.print(\n \"Unmatched case, value: {d}\\n\", .{@as(u8, 1)}\n );\n },\n}\n```\n:::\n\n\n\n\n\n### The `defer` keyword {#sec-defer}\n\nWith the `defer` keyword you can register an expression to be executed when you exit the current scope.\nTherefore, this keyword has a similar functionality as the `on.exit()` function from R.\nTake the `foo()` function below as an example. When we execute this `foo()` function, the expression\nthat prints the message \"Exiting function ...\" is getting executed only when the function exits\nits scope.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn foo() !void {\n defer std.debug.print(\n \"Exiting function ...\\n\", .{}\n );\n try stdout.print(\"Adding some numbers ...\\n\", .{});\n const x = 2 + 2; _ = x;\n try stdout.print(\"Multiplying ...\\n\", .{});\n const y = 2 * 8; _ = y;\n}\n\npub fn main() !void {\n try foo();\n}\n```\n:::\n\n\n\n\n```\nAdding some numbers ...\nMultiplying ...\nExiting function ...\n```\n\nTherefore, we can use `defer` to declare an expression that is going to be executed\nwhen your code exits the current scope. Some programmers like to interpret the phrase \"exit of the current scope\"\nas \"the end of the current scope\". But this interpretation might not be entirely correct, depending\non what you consider as \"the end of the current scope\".\n\nI mean, what do you consider as **the end** of the current scope? Is it the closing curly bracket (`}`) of the scope?\nIs it when the last expression in the function get's executed? Is it when the function returns to the previous scope?\nEtc. For example, it would not be correct to interpret the \"exit of the current scope\" as the closing\ncurly bracket of the scope. Because the function might exit from an earlier position than this\nclosing curly bracket (e.g. an error value was generated at a previous line inside the function;\nthe function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.\n\nNow, if you remember of what we have discussed in @sec-blocks, there are multiple structures in the language\nthat create their own separate scopes. For/while loops, if/else statements,\nfunctions, normal blocks, etc. This also affects the interpretation of `defer`.\nFor example, if you use `defer` inside a for loop, then, the given expression\nwill be executed everytime this specific for loop exits its own scope.\n\nBefore we continue, is worth emphasizing that the `defer` keyword is an \"unconditional defer\".\nWhich means that the given expression will be executed no matter how the code exits\nthe current scope. For example, your code might exit the current scope because of an error value\nbeing generated, or, because of a return statement, or, a break statement, etc.\n\n\n\n### The `errdefer` keyword {#sec-errdefer1}\n\nOn the previous section, we have discussed the `defer` keyword, which you can use to\nregister an expression to be executed at the exit of the current scope.\nBut this keyword has a brother, which is the `errdefer` keyword. While `defer`\nis an \"unconditional defer\", the `errdefer` keyword is a \"conditional defer\".\nWhich means that the given expression is executed only when you exit the current\nscope on a very specific circumstance.\n\nIn more details, the expression given to `errdefer` is executed only when an error occurs in the current scope.\nTherefore, if the function (or for/while loop, if/else statement, etc.) exits the current scope\nin a normal situation, without errors, the expression given to `errdefer` is not executed.\n\nThis makes the `errdefer` keyword one of the many tools available in Zig for error handling.\nIn this section, we are more concerned with the control flow aspects around `errdefer`.\nBut we are going to discuss `errdefer` later as a error handling tool in @sec-errdefer2.\n\nThe code example below demonstrates three things:\n\n- that `defer` is an \"unconditional defer\", because the given expression get's executed regardless of how the function `foo()` exits its own scope.\n- that `errdefer` is executed because the function `foo()` returned an error value.\n- that `defer` and `errdefer` expressions are executed in a LIFO (*last in, first out*) order.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n defer i = 2;\n try foo();\n}\n```\n:::\n\n\n\n\n```\nValue of i: 2\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\nWhen I say that \"defer expressions\" are executed in a LIFO order, what I want to say is that\nthe last `defer` or `errdefer` expressions in the code are the first ones to be executed.\nYou could also interpret this as: \"defer expressions\" are executed from bottom to top, or,\nfrom last to first.\n\nTherefore, if I change the order of the `defer` and `errdefer` expressions, you will notice that\nthe value of `i` that get's printed to the console changes to 1. This doesn't mean that the\n`defer` expression was not executed in this case. This actually means that the `defer` expression\nwas executed only after the `errdefer` expression. The code example below demonstrates this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n defer i = 2;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n try foo();\n}\n```\n:::\n\n\n\n\n```\nValue of i: 1\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\n\n\n### For loops\n\nA loop allows you to execute the same lines of code multiple times,\nthus, creating a \"repetition space\" in the execution flow of your program.\nLoops are particularly useful when we want to replicate the same function\n(or the same set of commands) over different inputs.\n\nThere are different types of loops available in Zig. But the most\nessential of them all is probably the *for loop*. A for loop is\nused to apply the same piece of code over the elements of a slice, or, an array.\n\nFor loops in Zig use a syntax that may be unfamiliar to programmers coming from\nother languages. You start with the `for` keyword, then, you\nlist the items that you want to iterate\nover inside a pair of parentheses. Then, inside of a pair of pipes (`|`)\nyou should declare an identifier that will serve as your iterator, or,\nthe \"repetition index of the loop\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (items) |value| {\n // code to execute\n}\n```\n:::\n\n\n\n\nTherefore, instead of using a `(value in items)` syntax,\nin Zig, for loops use the syntax `(items) |value|`. In the example\nbelow, you can see that we are looping through the items\nof the array stored at the object `name`, and printing to the\nconsole the decimal representation of each character in this array.\n\nIf we wanted, we could also iterate through a slice (or a portion) of\nthe array, instead of iterating through the entire array stored in the `name` object.\nJust use a range selector to select the section you want. For example,\nI could provide the expression `name[0..3]` to the for loop, to iterate\njust through the first 3 elements in the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name = [_]u8{'P','e','d','r','o'};\nfor (name) |char| {\n try stdout.print(\"{d} | \", .{char});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n80 | 101 | 100 | 114 | 111 | \n```\n\n\n:::\n:::\n\n\n\n\nIn the above example we are using the value itself of each\nelement in the array as our iterator. But there are many situations where\nwe need to use an index instead of the actual values of the items.\n\nYou can do that by providing a second set of items to iterate over.\nMore precisely, you provide the range selector `0..` to the for loop. So,\nyes, you can use two different iterators at the same time in a for\nloop in Zig.\n\nBut remember from @sec-assignments that, every object\nyou create in Zig must be used in some way. So if you declare two iterators\nin your for loop, you must use both iterators inside the for loop body.\nBut if you want to use just the index iterator, and not use the \"value iterator\",\nthen, you can discard the value iterator by maching the\nvalue items to the underscore character, like in the example below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (name, 0..) |_, i| {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n:::\n\n\n\n\n```\n0 | 1 | 2 | 3 | 4 |\n```\n\n\n### While loops\n\nA while loop is created from the `while` keyword. A `for` loop\niterates through the items of an array, but a `while` loop\nwill loop continuously, and infinitely, until a logical test\n(specified by you) becomes false.\n\nYou start with the `while` keyword, then, you define a logical\nexpression inside a pair of parentheses, and the body of the\nloop is provided inside a pair of curly braces, like in the example below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) {\n try stdout.print(\"{d} | \", .{i});\n i += 1;\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\nYou can also specify the increment expression to be used at the beginning of a while loop.\nTo do that, we write the increment expression inside a pair of parentheses after a colon character (`:`).\nThe code example below demonstrates this other pattern.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) : (i += 1) {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\n### Using `break` and `continue`\n\nIn Zig, you can explicitly stop the execution of a loop, or, jump to the next iteration of the loop, by using\nthe keywords `break` and `continue`, respectively. The `while` loop presented in the next code example is,\nat first sight, an infinite loop. Because the logical value inside the parenthese will always be equal to `true`.\nBut what makes this `while` loop stop when the `i` object reaches the count\n10? It is the `break` keyword!\n\nInside the while loop, we have an if statement that is constantly checking if the `i` variable\nis equal to 10. Since we are incrementing the value of `i` at each iteration of the\nwhile loop, this `i` object will eventually be equal to 10, and when it is, the if statement\nwill execute the `break` expression, and, as a result, the execution of the while loop is stopped.\n\nNotice the use of the `expect()` function from the Zig Standard Library after the while loop.\nThis `expect()` function is an \"assert\" type of function.\nThis function checks if the logical test provided is equal to true. If so, the function do nothing.\nOtherwise (i.e. the logical test is equal to false), the function raises an assertion error.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: usize = 0;\nwhile (true) {\n if (i == 10) {\n break;\n }\n i += 1;\n}\ntry std.testing.expect(i == 10);\ntry stdout.print(\"Everything worked!\", .{});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nEverything worked!\n```\n\n\n:::\n:::\n\n\n\n\nSince this code example was executed successfully by the `zig` compiler,\nwithout raising any errors, we known that, after the execution of the while loop,\nthe `i` object is equal to 10. Because if it wasn't equal to 10, an error would have\nbeen raised by `expect()`.\n\nNow, in the next example, we have a use case for\nthe `continue` keyword. The if statement is constantly\nchecking if the current index is a multiple of 2. If\nit is, we jump to the next iteration of the loop.\nOtherwise, the loop just prints the current index to the console.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [_]u8{1,2,3,4,5,6};\nfor (ns) |i| {\n if ((i % 2) == 0) {\n continue;\n }\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 3 | 5 | \n```\n\n\n:::\n:::\n\n\n\n\n\n\n## Function parameters are immutable {#sec-fun-pars}\n\nWe have already discussed a lot of the syntax behind function declarations in @sec-root-file and @sec-main-file.\nBut I want to emphasize a curious fact about function parameters (a.k.a. function arguments) in Zig.\nIn summary, function parameters are immutable in Zig.\n\nTake the code example below, where we declare a simple function that just tries to add\nsome amount to the input integer, and returns the result back. If you look closely\nat the body of this `add2()` function, you will notice that we try\nto save the result back into the `x` function argument.\n\nIn other words, this function not only uses the value that it received through the function argument\n`x`, but it also tries to change the value of this function argument, by assigning the addition result\ninto `x`. However, function arguments in Zig are immutable. You cannot change their values, or, you\ncannot assign values to them inside the body's function.\n\nThis is the reason why, the code example below does not compile successfully. If you try to compile\nthis code example, you will get a compile error message about \"trying to change the value of a\nimmutable (i.e. constant) object\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: u32) u32 {\n x = x + 2;\n return x;\n}\n\npub fn main() !void {\n const y = add2(4);\n std.debug.print(\"{d}\\n\", .{y});\n}\n```\n:::\n\n\n\n\n```\nt.zig:3:5: error: cannot assign to constant\n x = x + 2;\n ^\n```\n\n\n### A free optimization\n\nIf a function argument receives as input an object whose data type is\nany of the primitive types that we have listed in @sec-primitive-data-types,\nthis object is always passed by value to the function. In other words, this object\nis copied into the function stack frame.\n\nHowever, if the input object have a more complex data type, for example, it might\nbe a struct instance, or an array, or an union value, etc., in cases like that, the `zig` compiler\nwill take the liberty of deciding for you which strategy is best. Thus, the `zig` compiler will\npass your object to the function either by value, or by reference. The compiler will always\nchoose the strategy that is faster for you.\nThis optimization that you get for free is possible only because function arguments are\nimmutable in Zig.\n\n\n### How to overcome this barrier\n\nThere are some situations where you might need to change the value of your function argument\ndirectly inside the function's body. This happens more often when we are passing\nC structs as inputs to Zig functions.\n\nIn a situation like this, you can overcome this barrier by using a pointer. In other words,\ninstead of passing a value as input to the argument, you can pass a \"pointer to value\" instead.\nYou can change the value that the pointer points to, by dereferencing it.\n\nTherefore, if we take our previous `add2()` example, we can change the value of the\nfunction argument `x` inside the function's body by marking the `x` argument as a\n\"pointer to a `u32` value\" (i.e. `*u32` data type), instead of a `u32` value.\nBy making it a pointer, we can finally alter the value of this function argument directly inside\nthe body of the `add2()` function. You can see that the code example below compiles successfully.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: *u32) void {\n const d: u32 = 2;\n x.* = x.* + d;\n}\n\npub fn main() !void {\n var x: u32 = 4;\n add2(&x);\n std.debug.print(\"Result: {d}\\n\", .{x});\n}\n```\n:::\n\n\n\n\n```\nResult: 6\n```\n\n\nEven in this code example above, the `x` argument is still immutable. Which means that the pointer itself is immutable.\nTherefore, you cannot change the memory address that it points to. However, you can dereference the pointer\nto access the value that it points to, and also, to change this value, if you need to.\n\n\n\n\n\n## Structs and OOP {#sec-structs-and-oop}\n\nZig is a language more closely related to C (which is a procedural language),\nthan it is to C++ or Java (which are object-oriented languages). Because of that, you do not\nhave advanced OOP (Object-Oriented Programming) patterns available in Zig, such as classes, interfaces or\nclass inheritance. Nonetheless, OOP in Zig is still possible by using struct definitions.\n\nWith struct definitions, you can create (or define) a new data type in Zig. These struct definitions work the same way as they work in C.\nYou give a name to this new struct (or, to this new data type you are creating), then, you list the data members of this new struct. You can\nalso register functions inside this struct, and they become the methods of this particular struct (or data type), so that, every object\nthat you create with this new type, will always have these methods available and associated with them.\n\nIn C++, when we create a new class, we normally have a constructor method (or, a constructor function) which\nis used to construct (or, to instantiate) every object of this particular class, and we also have\na destructor method (or a destructor function), which is the function responsible for destroying\nevery object of this class.\n\nIn Zig, we normally declare the constructor and the destructor methods\nof our structs, by declaring an `init()` and a `deinit()` methods inside the struct.\nThis is just a naming convention that you will find across the entire Zig Standard Library.\nSo, in Zig, the `init()` method of a struct is normally the constructor method of the class represented by this struct.\nWhile the `deinit()` method is the method used for destroying an existing instance of that struct.\n\nThe `init()` and `deinit()` methods are both used extensively in Zig code, and you will see both of\nthem being used when we talk about allocators in @sec-allocators.\nBut, as another example, let's build a simple `User` struct to represent a user of some sort of system.\n\nIf you look at the `User` struct below, you can see the `struct` keyword.\nNotice the data members of this struct: `id`, `name` and `email`. Every data member has its\ntype explicitly annotated, with the colon character (`:`) syntax that we described earlier in @sec-root-file.\nBut also notice that every line in the struct body that describes a data member, ends with a comma character (`,`).\nSo every time you declare a data member in your Zig code, always end the line with a comma character, instead\nof ending it with the traditional semicolon character (`;`).\n\nNext, we have registered an `init()` function as a method\nof this `User` struct. This `init()` method is the constructor method that we will use to instantiate\nevery new `User` object. That is why this `init()` function returns a new `User` object as result.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst User = struct {\n id: u64,\n name: []const u8,\n email: []const u8,\n\n pub fn init(id: u64,\n name: []const u8,\n email: []const u8) User {\n\n return User {\n .id = id,\n .name = name,\n .email = email\n };\n }\n\n pub fn print_name(self: User) !void {\n try stdout.print(\"{s}\\n\", .{self.name});\n }\n};\n\npub fn main() !void {\n const u = User.init(1, \"pedro\", \"email@gmail.com\");\n try u.print_name();\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\npedro\n```\n\n\n:::\n:::\n\n\n\n\nThe `pub` keyword plays an important role in struct declarations, and OOP in Zig.\nEvery method that you declare in your struct that is marked with the keyword `pub`,\nbecomes a public method of this particular struct.\n\nSo every method that you create inside your struct, is, at first, a private method\nof that struct. Meaning that, this method can only be called from within this\nstruct. But, if you mark this method as public, with the keyword `pub`, then,\nyou can call the method directly from an instance of the `User` struct.\n\nIn other words, the functions marked by the keyword `pub`\nare members of the public API of that struct.\nFor example, if I did not mark the `print_name()` method as public,\nthen, I could not execute the line `u.print_name()`. Because I would\nnot be authorized to call this method directly in my code.\n\n\n\n### Anonymous struct literals {#sec-anonymous-struct-literals}\n\nYou can declare a struct object as a literal value. When we do that, we normally specify the\ndata type of this struct literal by writing its data type just before the opening curly brace.\nFor example, I could write a struct literal value of the type `User` that we have defined\nin the previous section like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst eu = User {\n .id = 1,\n .name = \"Pedro\",\n .email = \"someemail@gmail.com\"\n};\n_ = eu;\n```\n:::\n\n\n\n\nHowever, in Zig, we can also write an anonymous struct literal. That is, you can write a\nstruct literal, but not specify explicitly the type of this particular struct.\nAn anonymous struct is written by using the syntax `.{}`. So, we essentially\nreplaced the explicit type of the struct literal with a dot character (`.`).\n\nAs we described in @sec-type-inference, when you put a dot before a struct literal,\nthe type of this struct literal is automatically inferred by the `zig` compiler.\nIn essence, the `zig` compiler will look for some hint of what is the type of that struct.\nThis hint can be the type annotation of a function argument,\nor the return type annotation of the function that you are using, or the type annotation\nof an existing object.\nIf the compiler does find such type annotation, it will use this\ntype in your literal struct.\n\nAnonymous structs are very commonly used as inputs to function arguments in Zig.\nOne example that you have seen already constantly, is the `print()`\nfunction from the `stdout` object.\nThis function takes two arguments.\nThe first argument, is a template string, which should\ncontain string format specifiers in it, which tells how the values provided\nin the second argument should be printed into the message.\n\nWhile the second argument is a struct literal that lists the values\nto be printed into the template message specified in the first argument.\nYou normally want to use an anonymous struct literal here, so that the\n`zig` compiler do the job of specifying the type of this particular\nanonymous struct for you.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHello, world!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Struct declarations must be constant\n\nTypes in Zig must be `const` or `comptime` (we are going to talk more about comptime in @sec-comptime).\nWhat this means is that you cannot create a new data type, and mark it as variable with the `var` keyword.\nSo struct declarations are always constant. You cannot declare a new struct type using the `var` keyword.\nIt must be `const`.\n\nIn the `Vec3` example below, this declaration is allowed because I'm using the `const` keyword\nto declare this new data type.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n};\n```\n:::\n\n\n\n\n\n### The `self` method argument {#sec-self-arg}\n\nIn every language that have OOP, when we declare a method of some class or struct, we\nusually declare this method as a function that has a `self` argument.\nThis `self` argument is the reference to the object itself from which the method\nis being called from.\n\nIt is not mandatory to use this `self` argument. But why would you not use this `self` argument?\nThere is no reason to not use it. Because the only way to get access to the data stored in the\ndata members of your struct is to access them through this `self` argument.\nIf you don't need to use the data in the data members of your struct inside your method, you very likely don't need\na method. You can just declare this logic as a simple function, outside of your\nstruct declaration.\n\n\nTake the `Vec3` struct below. Inside this `Vec3` struct we declared a method named `distance()`.\nThis method calculates the distance between two `Vec3` objects, by following the distance\nformula in euclidean space. Notice that this `distance()` method takes two `Vec3` objects\nas input, `self` and `other`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n};\n```\n:::\n\n\n\n\n\nThe `self` argument corresponds to the `Vec3` object from which this `distance()` method\nis being called from. While the `other` is a separate `Vec3` object that is given as input\nto this method. In the example below, the `self` argument corresponds to the object\n`v1`, because the `distance()` method is being called from the `v1` object,\nwhile the `other` argument corresponds to the object `v2`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst v1 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nconst v2 = Vec3 {\n .x = 5.1, .y = 5.6, .z = 1.6\n};\n\nstd.debug.print(\n \"Distance: {d}\\n\",\n .{v1.distance(v2)}\n);\n```\n:::\n\n\n\n\n```\nDistance: 3.3970575502926055\n```\n\n\n\n### About the struct state\n\nSometimes you don't need to care about the state of your struct object. Sometimes, you just need\nto instantiate and use the objects, without altering their state. You can notice that when you have methods\ninside your struct declaration that might use the values that are present in the data members, but they\ndo not alter the values in these data members of the struct in anyway.\n\nThe `Vec3` struct that was presented in @sec-self-arg is an example of that.\nThis struct have a single method named `distance()`, and this method does use the values\npresent in all three data members of the struct (`x`, `y` and `z`). But at the same time,\nthis method does not change the values of these data members at any point.\n\nAs a result of that, when we create `Vec3` objects we usually create them as\nconstant objects, like the `v1` and `v2` objects presented in @sec-self-arg.\nWe can create them as variable objects with the `var` keyword,\nif we want to. But because the methods of this `Vec3` struct do not change\nthe state of the objects in any point, it's unnecessary to mark them\nas variable objects.\n\nBut why? Why am I talking about this here? It's because the `self` argument\nin the methods is affected depending on whether the\nmethods present in a struct change or don't change the state of the object itself.\nMore specifically, when you have a method in a struct that changes the state\nof the object (i.e. change the value of a data member), the `self` argument\nin this method must be annotated in a different manner.\n\nAs I described in @sec-self-arg, the `self` argument in methods of\na struct is the argument that receives as input the object from which the method\nwas called from. We usually annotate this argument in the methods by writing `self`,\nfollowed by the colon character (`:`), and the data type of the struct to which\nthe method belongs to (e.g. `User`, `Vec3`, etc.).\n\nIf we take the `Vec3` struct that we defined in the previous section as an example,\nwe can see in the `distance()` method that this `self` argument is annotated as\n`self: Vec3`. Because the state of the `Vec3` object is never altered by this\nmethod.\n\nBut what if we do have a method that alters the state of the object, by altering the\nvalues of its data members, how should we annotate `self` in this instance? The answer is:\n\"we should annotate `self` as a pointer of `x`, instead of just `x`\".\nIn other words, you should annotate `self` as `self: *x`, instead of annotating it\nas `self: x`.\n\nIf we create a new method inside the `Vec3` object that, for example, expands the\nvector by multiplying its coordinates by a factor of two, then, we need to follow\nthis rule specified in the previous paragraph. The code example below demonstrates\nthis idea:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n\n pub fn twice(self: *Vec3) void {\n self.x = self.x * 2.0;\n self.y = self.y * 2.0;\n self.z = self.z * 2.0;\n }\n};\n```\n:::\n\n\n\n\nNotice in the code example above that we have added a new method\nto our `Vec3` struct named `twice()`. This method doubles the\ncoordinate values of our vector object. In the\ncase of the `twice()` method, we annotated the `self` argument as `*Vec3`,\nindicating that this argument receives a pointer (or a reference, if you prefer to call it this way)\nto a `Vec3` object as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar v3 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nv3.twice();\nstd.debug.print(\"Doubled: {d}\\n\", .{v3.x});\n```\n:::\n\n\n\n\n```\nDoubled: 8.4\n```\n\n\n\nNow, if you change the `self` argument in this `twice()` method to `self: Vec3`, like in the\n`distance()` method, you will get the compiler error exposed below as result. Notice that this\nerror message is showing a line from the `twice()` method body,\nindicating that you cannot alter the value of the `x` data member.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// If we change the function signature of double to:\n pub fn twice(self: Vec3) void {\n```\n:::\n\n\n\n\n```\nt.zig:16:13: error: cannot assign to constant\n self.x = self.x * 2.0;\n ~~~~^~\n```\n\nThis error message indicates that the `x` data member belongs to a constant object,\nand, because of that, it cannot be changed. Ultimately, this error message\nis telling us that the `self` argument is constant.\n\nIf you take some time, and think hard about this error message, you will understand it.\nYou already have the tools to understand why we are getting this error message.\nWe have talked about it already in @sec-fun-pars.\nSo remember, every function argument is immutable in Zig, and `self`\nis no exception to this rule.\n\nIn this example, we marked the `v3` object as a variable object.\nBut this does not matter. Because it is not about the input object, it is about\nthe function argument.\n\nThe problem begins when we try to alter the value of `self` directly, which is a function argument,\nand, every function argument is immutable by default. You may ask yourself how can we overcome\nthis barrier, and once again, the solution was also discussed in @sec-fun-pars.\nWe overcome this barrier, by explicitly marking the `self` argument as a pointer.\n\n\n::: {.callout-note}\nIf a method of your `x` struct alters the state of the object, by\nchanging the value of any data member, then, remember to use `self: *x`,\ninstead of `self: x` in the function signature of this method.\n:::\n\n\nYou could also interpret the content discussed in this section as:\n\"if you need to alter the state of your `x` struct object in one of its methods,\nyou must explicitly pass the `x` struct object by reference to the `self` argument of this method\".\n\n\n\n## Type inference {#sec-type-inference}\n\nZig is a strongly typed language. But, there are some situations\nwhere you don't have to explicitly write the type of every single object in your source code,\nas you would expect from a traditional strongly typed language, such as C and C++.\n\nIn some situations, the `zig` compiler can use type inference to solve the data types for you, easing some of\nthe burden that you carry as a developer.\nThe most common way this happens is through function arguments that receive struct objects\nas input.\n\nIn general, type inference in Zig is done by using the dot character (`.`).\nEverytime you see a dot character written before a struct literal, or before an enum value, or something like that,\nyou know that this dot character is playing a special party in this place. More specifically, it is\ntelling the `zig` compiler something along the lines of: \"Hey! Can you infer the type of this\nvalue for me? Please!\". In other words, this dot character is playing a similar role as the `auto` keyword in C++.\n\nI gave you some examples of this in @sec-anonymous-struct-literals, where we used anonymous struct literals.\nAnonymous struct literals are, struct literals that use type inference to\ninfer the exact type of this particular struct literal.\nThis type inference is done by looking for some minimal hint of the correct data type to be used.\nYou could say that the `zig` compiler looks for any neighbouring type annotation that might tell it\nwhat the correct type would be.\n\nAnother common place where we use type inference in Zig is at switch statements (which we talked about in @sec-switch).\nI also gave some other examples of type inference in @sec-switch, where we were inferring the data types of enum values listed inside\nof switch statements (e.g. `.DE`).\nBut as another example, take a look at this `fence()` function reproduced below,\nwhich comes from the [`atomic.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/atomic.zig)[^fence-fn]\nof the Zig Standard Library.\n\n[^fence-fn]: .\n\nThere are a lot of things in this function that we haven't talked about yet, such as:\nwhat `comptime` means? `inline`? `extern`?\nLet's just ignore all of these things, and focus solely on the switch statement\nthat is inside this function.\n\nWe can see that this switch statement uses the `order` object as input. This `order`\nobject is one of the inputs of this `fence()` function, and we can see in the type annotation,\nthat this object is of type `AtomicOrder`. We can also see a bunch of values inside the\nswitch statements that begin with a dot character, such as `.release` and `.acquire`.\n\nBecause these weird values contain a dot character before them, we are asking the `zig`\ncompiler to infer the types of these values inside the switch statement. Then, the `zig`\ncompiler is looking into the current context where these values are being used, and it is\ntrying to infer the types of these values.\n\nSince they are being used inside a switch statement, the `zig` compiler looks into the type\nof the input object given to the switch statement, which is the `order` object in this case.\nBecause this object have type `AtomicOrder`, the `zig` compiler infers that these values\nare data members from this type `AtomicOrder`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub inline fn fence(self: *Self, comptime order: AtomicOrder) void {\n // many lines of code ...\n if (builtin.sanitize_thread) {\n const tsan = struct {\n extern \"c\" fn __tsan_acquire(addr: *anyopaque) void;\n extern \"c\" fn __tsan_release(addr: *anyopaque) void;\n };\n\n const addr: *anyopaque = self;\n return switch (order) {\n .unordered, .monotonic => @compileError(\n @tagName(order)\n ++ \" only applies to atomic loads and stores\"\n ),\n .acquire => tsan.__tsan_acquire(addr),\n .release => tsan.__tsan_release(addr),\n .acq_rel, .seq_cst => {\n tsan.__tsan_acquire(addr);\n tsan.__tsan_release(addr);\n },\n };\n }\n\n return @fence(order);\n}\n```\n:::\n\n\n\n\nThis is how basic type inference is done in Zig. If we didn't use the dot character before\nthe values inside this switch statement, then, we would be forced to explicitly write\nthe data types of these values. For example, instead of writing `.release` we would have to\nwrite `AtomicOrder.release`. We would have to do this for every single value\nin this switch statement, and this is a lot of work. That is why type inference\nis commonly used on switch statements in Zig.\n\n\n\n## Type casting {#sec-type-cast}\n\nIn this section, I want to discuss type casting (or, type conversion) with you.\nWe use type casting when we have an object of type \"x\", and we want to convert\nit into an object of type \"y\", i.e. we want to change the data type of the object.\n\nMost languages have a formal way to perform type casting. In Rust for example, we normally\nuse the keyword `as`, and in C, we normally use the type casting syntax, e.g. `(int) x`.\nIn Zig, we use the `@as()` built-in function to cast an object of type \"x\", into\nan object of type \"y\".\n\nThis `@as()` function is the preferred way to perform type conversion (or type casting)\nin Zig. Because it is explicit, and, it also performs the casting only if it\nis unambiguous and safe. To use this function, you just provide the target data type\nin the first argument, and, the object that you want cast as the second argument.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 500;\n const y = @as(u32, x);\n try expect(@TypeOf(y) == u32);\n}\n```\n:::\n\n\n\n\nThis is the general way to perform type casting in Zig. But remember, `@as()` works only when casting\nis unambiguous and safe, and there are situations where these assumptions do not hold. For example,\nwhen casting an integer value into a float value, or vice-versa, it is not clear to the compiler\nhow to perform this conversion safely.\n\nTherefore, we need to use specialized \"casting functions\" in such situations.\nFor example, if you want to cast an integer value into a float value, then, you\nshould use the `@floatFromInt()` function. In the inverse scenario, you should use\nthe `@intFromFloat()` function.\n\nIn these functions, you just provide the object that you want to\ncast as input. Then, the target data type of the \"type casting operation\" is determined by\nthe type annotation of the object where you are saving the results.\nIn the example below, we are casting the object `x` into a value of type `f32`,\nbecause the object `y`, which is where we are saving the results, is annotated\nas an object of type `f32`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 565;\n const y: f32 = @floatFromInt(x);\n try expect(@TypeOf(y) == f32);\n}\n```\n:::\n\n\n\n\nAnother built-in function that is very useful when performing type casting operations is `@ptrCast()`.\nIn essence, we use the `@as()` built-in function when we want to explicit convert (or cast) a Zig value/object\nfrom a type \"x\" to a type \"y\", etc. However, pointers (we are going to discuss pointers\nin more depth in @sec-pointer) are a special type of object in Zig,\ni.e. they are treated differently from \"normal objects\".\n\nEverytime a pointer is involved in some \"type casting operation\" in Zig, the `@ptrCast()` function is used.\nThis function works similarly to `@floatFromInt()`.\nYou just provide the pointer object that you want to cast as input to this function, and the\ntarget data type is, once again, determined by the type annotation of the object where the results are being\nstored.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const bytes align(@alignOf(u32)) = [_]u8{\n 0x12, 0x12, 0x12, 0x12\n };\n const u32_ptr: *const u32 = @ptrCast(&bytes);\n try expect(@TypeOf(u32_ptr) == *const u32);\n}\n```\n:::\n\n\n\n\n\n\n\n\n## Modules\n\nWe already talked about what modules are, and also, how to import other modules into\nyour current module via *import statements*. Every Zig module (i.e. a `.zig` file) that you write in your project\nis internally stored as a struct object. Take the line exposed below as an example. In this line we are importing the\nZig Standard Library into our current module.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n```\n:::\n\n\n\n\nWhen we want to access the functions and objects from the standard library, we\nare basically accessing the data members of the struct stored in the `std`\nobject. That is why we use the same syntax that we use in normal structs, with the dot operator (`.`)\nto access the data members and methods of the struct.\n\nWhen this \"import statement\" get's executed, the result of this expression is a struct\nobject that contains the Zig Standard Library modules, global variables, functions, etc.\nAnd this struct object get's saved (or stored) inside the constant object named `std`.\n\n\nTake the [`thread_pool.zig` module from the project `zap`](https://github.com/kprotty/zap/blob/blog/src/thread_pool.zig)[^thread]\nas an example. This module is written as if it was\na big struct. That is why we have a top-level and public `init()` method\nwritten in this module. The idea is that all top-level functions written in this\nmodule are methods from the struct, and all top-level objects and struct declarations\nare data members of this struct. The module is the struct itself.\n\n[^thread]: \n\n\nSo you would import and use this module by doing something like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst ThreadPool = @import(\"thread_pool.zig\");\nconst num_cpus = std.Thread.getCpuCount()\n catch @panic(\"failed to get cpu core count\");\nconst num_threads = std.math.cast(u16, num_cpus)\n catch std.math.maxInt(u16);\nconst pool = ThreadPool.init(\n .{ .max_threads = num_threads }\n);\n```\n:::\n",
+ "supporting": [
+ "03-structs_files"
+ ],
"filters": [
"rmarkdown/pagebreak.lua"
],
diff --git a/docs/Chapters/03-structs.html b/docs/Chapters/03-structs.html
index e4dfa23f..a9014775 100644
--- a/docs/Chapters/03-structs.html
+++ b/docs/Chapters/03-structs.html
@@ -464,7 +464,12 @@
In contrast, if the type of this struct that you are declaring, includes a data member that is an array, but this array has a known fixed size, like [60]u8 (which declares an array of 60 u8 values), then, this type, or, this struct that you are declaring, becomes a type with a known fixed size at compile-time. And because of that, in this case, the zig compiler does not need to know at compile-time the exact value of any object of this type. Since the compiler can find the necessary size to store this object by looking at the size of its type.
Let’s look at an example. In the source code below, we have two constant objects (name and array) declared. Because the values of these particular objects are written down, in the source code itself ("Pedro" and the number sequence from 1 to 4), the zig compiler can easily discover the values of these constant objects (name and array) during the compilation process. This is what “known at compile time” means. It refers to any object that you have in your Zig source code whose value can be identified at compile time.
The other side of the spectrum are objects whose values are not known at compile time. Function arguments are a classic example of this. Because the value of each function argument depends on the value that you assign to this particular argument, when you call the function.
For example, the function input_length() contains an argument named input, which is an array of constant u8 integers ([]const u8). It is impossible to know the value of this particular argument at compile time. And it also is impossible to know the size/length of this particular argument. Because it is an array that do not have a fixed size specified explicitly in the argument type annotation.
@@ -401,13 +403,15 @@
Every time you make a function call in Zig, an amount of space in the stack is reserved for this particular function call (Chen and Guo 2022; Zig Software Foundation 2024). The value of each function argument given to the function in this function call is stored in this stack space. Also, every local object that you declare inside the function scope is usually stored in this same stack space.
Looking at the example below, the object result is a local object declared inside the scope of the add() function. Because of that, this object is stored inside the stack space reserved for the add() function. The r object (which is declared outside of the add() function scope) is also stored in the stack. But since it is declared in the “outer” scope, this object is stored in the stack space that belongs to this outer scope.
-
const r = add(5, 27);
-_ = r;
-
-fn add(x: u8, y: u8) u8{
-const result = x + y;
-return result;
-}
+
fn add(x: u8, y: u8) u8{
+const result = x + y;
+return result;
+}
+
+pubfn main() !void{
+const r = add(5, 27);
+ _ = r;
+}
So, any object that you declare inside the scope of a function is always stored inside the space that was reserved for that particular function in the stack memory. This also counts for any object declared inside the scope of your main() function for example. As you would expect, in this case, they are stored inside the stack space reserved for the main() function.
One very important detail about the stack memory is that it frees itself automatically. This is very important, remember that. When objects are stored in the stack memory, you don’t have the work (or the responsibility) of freeing/destroying these objects. Because they will be automatically destroyed once the stack space is freed at the end of the function scope.
@@ -444,16 +448,16 @@
So, using again the add() function as an example, if you rewrite this function so that it returns a pointer to the local object result, the zig compiler will actually compile your program, with no warnings or errors. At first glance, it looks that this is good code that works as expected. But this is a lie!
If you try to take a look at the value inside of the r object, or, if you try to use this r object in another expression or function call, then, you would have undefined behaviour, and major bugs in your program (Zig Software Foundation 2024, see “Lifetime and Ownership”3 and “Undefined Behaviour”4 sections).
-
// This code compiles successfully. But it has
-// undefined behaviour. Never do this!!!
-
-// The `r` object is undefined!
-const r = add(5, 27);
-_ = r;
-
-fn add(x: u8, y: u8) *constu8{
-const result = x + y;
-return &result;
+
fn add(x: u8, y: u8) *constu8{
+const result = x + y;
+return &result;
+}
+
+pubfn main() !void{
+// This code compiles successfully. But it has
+// undefined behaviour. Never do this!!!
+// The `r` object is undefined!
+const r = add(5, 27); _ = r;}
This “invalid pointer to stack variable” problem is very known across many programming language communities. If you try to do the same thing, for example, in a C or C++ program (i.e. returning an address to a local object stored in the stack), you would also get undefined behaviour in the program.
Type 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons
+ st [4]i32Type 4: []const u8
+
Type 1: [4]i32
Type 2: *const [16:0]u8
@@ -918,17 +922,17 @@
The encoding UTF-8 works by assigning a number (which is called a unicode point) to each character in the string. For example, the character “H” is stored in UTF-8 as the decimal number 72. This means that the number 72 is the unicode point for the character “H”. Each possible character that can appear in a UTF-8 encoded string have its own unicode point.
For example, the Latin Capital Letter A With Stroke (Ⱥ) is represented by the number (or the unicode point) 570. However, this decimal number (570) is higher than the maximum number stored inside a single byte, which is 255. In other words, the maximum decimal number that can be represented with a single byte is 255. That is why, the unicode point 570 is actually stored inside the computer’s memory as the bytes C8 BA.
If you need to iterate through the characters of a string, instead of its bytes, then, you can use the std.unicode.Utf8View struct to create an iterator that iterates through the unicode points of your string.
In the example below, we loop through the japanese characters “アメリカ”. Each of the four characters in this string is represented by three bytes. But the for loop iterates four times, one iteration for each character/unicode point in this string:
The concat() function, as the name suggests, concatenate two or more strings together. Because the process of concatenating the strings involves allocating enough space to accomodate all the strings together, this concat() function receives an allocator object as input.
As you can imagine, the replace() function is used to replace substrings in a string by another substring. This function works very similarly to the replace() method from Python strings. Therefore, you provide a substring to search, and every time that the replace() function finds this substring within the input string, it replaces this substring with the “replacement substring” that you provided as input.
In the example below, we are taking the input string “Hello”, and replacing all occurrences of the substring “el” inside this input string with “34”, and saving the results inside the buffer object. As result, the replace() function returns an usize value that indicates how many replacements were performed.
const result = add(34, 16); std.debug.print("Result: {d}\n", .{result});}
+
+
Result: 50
+
Result: 50
You could also achieve the exact same result by getting a file descriptor object to stderr, then, creating a writer object to stderr, then, using the print() method of this writer object, like in the example below:
-
const std = @import("std");
-const stderr = std.io.getStdErr().writer();
-// some more lines ...
-_ = try stderr.print("Result: {d}", .{result});
+
const std = @import("std");
+const stderr = std.io.getStdErr().writer();
+// some more lines ...
+_ = try stderr.print("Result: {d}", .{result});
@@ -379,83 +382,83 @@
5.2.2 Let’s debug a program
As an example, let’s use LLDB to navigate and investigate the following piece of Zig code:
There is nothing wrong with this program. But it is a good start for us. First, we need to compile this program with the zig build-exe command. For this example, suppose that I have compiled the above Zig code into a binary executable called add_program.
-
zig build-exe add_program.zig
+
zig build-exe add_program.zig
Now, we can start LLDB with add_program, like this:
-
lldb add_program
+
lldb add_program
From now on, LLDB is started, and you can know that I’m executing LLDB commands by looking at the prefix (lldb). If something is prefixed with (lldb), then you know that it is a LLDB command.
The first thing I will do, is to set a breakpoint at the main() function, by executing b main. After that, I just start the execution of the program with run. You can see in the output below, that the execution stopped at the first line in the function main(), as we expected.
-
(lldb)b main
-Breakpoint 1: where = debugging`debug1.main + 22
-at debug1.zig:11:30, address = 0x00000000010341a6
-(lldb)run
-Process 8654 launched: 'add_program'(x86_64)
-Process 8654 stopped
-* thread #1, name = 'add_program',
-stop reason = breakpoint 1.1 frame #0: 0x10341a6
-add_program`debug1.main at add_program.zig:11:30
-8 }
-9
-10 pub fn main()!void {
--> 11 var n = add_and_increment(2, 3);
-12 n = add_and_increment(n, n);
-13 try stdout.print("Result: {d}!\n", .{n});
-14 }
+
(lldb)b main
+Breakpoint 1: where = debugging`debug1.main + 22
+at debug1.zig:11:30, address = 0x00000000010341a6
+(lldb)run
+Process 8654 launched: 'add_program'(x86_64)
+Process 8654 stopped
+* thread #1, name = 'add_program',
+stop reason = breakpoint 1.1 frame #0: 0x10341a6
+add_program`debug1.main at add_program.zig:11:30
+8 }
+9
+10 pub fn main()!void {
+-> 11 var n = add_and_increment(2, 3);
+12 n = add_and_increment(n, n);
+13 try stdout.print("Result: {d}!\n", .{n});
+14 }
I can start navigating through the code, and checking the objects that are being generated. If you are not familiar with the commands available in LLDB, I recommend you to read the official documentation of the project3. You can also look for cheat sheets, which quickly describes all commands available for you4.
Currently, we are in the first line at the main() function. In this line, we create the n object, by executing the add_and_increment() function. To execute the current line of code, and go to the next line, we can run the n LLDB command. Let’s execute this command.
After we executed this line, we can also look at the value stored inside this n object by using the p LLDB command. The syntax for this command is p <name-of-object>.
If we take a look at the value stored in the n object (p n), notice that it stores the hexadecimal value 0x06, which is the number 6 in decimal. We can also see that, this value have a type unsigned char, which is an unsigned 8-bit integer. We have talked already about this at Section 1.8, that u8 integers in Zig are equivalent to the C data type unsigned char.
-
(lldb)n
-Process 4798 stopped
-* thread #1, name = 'debugging',
-stop reason = step over frame #0: 0x10341ae
-debugging`debug1.main at debug1.zig:12:26
-9
-10 pub fn main()!void {
-11 var n = add_and_increment(2, 3);
--> 12 n = add_and_increment(n, n);
-13 try stdout.print("Result: {d}!\n", .{n});
-14 }
-(lldb)p n
-(unsigned char)$1 = '\x06'
+
(lldb)n
+Process 4798 stopped
+* thread #1, name = 'debugging',
+stop reason = step over frame #0: 0x10341ae
+debugging`debug1.main at debug1.zig:12:26
+9
+10 pub fn main()!void {
+11 var n = add_and_increment(2, 3);
+-> 12 n = add_and_increment(n, n);
+13 try stdout.print("Result: {d}!\n", .{n});
+14 }
+(lldb)p n
+(unsigned char)$1 = '\x06'
Now, on the next line of code, we are executing the add_and_increment() function once again. Why not step inside this function? Shall we? We can do that, by executing the s LLDB command. Notice in the example below that, after executing this command, we have entered into the context of the add_and_increment() function.
Also notice in the example below that, I have walked two more lines in the function’s body, then, I execute the frame variable LLDB command, to see at once, the value stored in each of the variables that were created inside the current scope.
You can see in the output below that, the object sum stores the value \f, which represents the form feed character. This character in the ASCII table, corresponds to the hexadecimal value 0x0C, or, in decimal, the number 12. So, this means that the result of the expression a + b executed at line 5, resulted in the number 12.
You can do that by providing a second set of items to iterate over. More precisely, you provide the range selector 0.. to the for loop. So, yes, you can use two different iterators at the same time in a for loop in Zig.
But remember from Section 1.4 that, every object you create in Zig must be used in some way. So if you declare two iterators in your for loop, you must use both iterators inside the for loop body. But if you want to use just the index iterator, and not use the “value iterator”, then, you can discard the value iterator by maching the value items to the underscore character, like in the example below:
Even in this code example above, the x argument is still immutable. Which means that the pointer itself is immutable. Therefore, you cannot change the memory address that it points to. However, you can dereference the pointer to access the value that it points to, and also, to change this value, if you need to.
@@ -694,33 +698,33 @@
If you look at the User struct below, you can see the struct keyword. Notice the data members of this struct: id, name and email. Every data member has its type explicitly annotated, with the colon character (:) syntax that we described earlier in Section 1.2.2. But also notice that every line in the struct body that describes a data member, ends with a comma character (,). So every time you declare a data member in your Zig code, always end the line with a comma character, instead of ending it with the traditional semicolon character (;).
Next, we have registered an init() function as a method of this User struct. This init() method is the constructor method that we will use to instantiate every new User object. That is why this init() function returns a new User object as result.
You can declare a struct object as a literal value. When we do that, we normally specify the data type of this struct literal by writing its data type just before the opening curly brace. For example, I could write a struct literal value of the type User that we have defined in the previous section like this:
However, in Zig, we can also write an anonymous struct literal. That is, you can write a struct literal, but not specify explicitly the type of this particular struct. An anonymous struct is written by using the syntax .{}. So, we essentially replaced the explicit type of the struct literal with a dot character (.).
As we described in Section 2.4, when you put a dot before a struct literal, the type of this struct literal is automatically inferred by the zig compiler. In essence, the zig compiler will look for some hint of what is the type of that struct. This hint can be the type annotation of a function argument, or the return type annotation of the function that you are using, or the type annotation of an existing object. If the compiler does find such type annotation, it will use this type in your literal struct.
Anonymous structs are very commonly used as inputs to function arguments in Zig. One example that you have seen already constantly, is the print() function from the stdout object. This function takes two arguments. The first argument, is a template string, which should contain string format specifiers in it, which tells how the values provided in the second argument should be printed into the message.
While the second argument is a struct literal that lists the values to be printed into the template message specified in the first argument. You normally want to use an anonymous struct literal here, so that the zig compiler do the job of specifying the type of this particular anonymous struct for you.
Section 12.1). What this means is that you cannot create a new data type, and mark it as variable with the var keyword. So struct declarations are always constant. You cannot declare a new struct type using the var keyword. It must be const.
In the Vec3 example below, this declaration is allowed because I’m using the const keyword to declare this new data type.
It is not mandatory to use this self argument. But why would you not use this self argument? There is no reason to not use it. Because the only way to get access to the data stored in the data members of your struct is to access them through this self argument. If you don’t need to use the data in the data members of your struct inside your method, you very likely don’t need a method. You can just declare this logic as a simple function, outside of your struct declaration.
Take the Vec3 struct below. Inside this Vec3 struct we declared a method named distance(). This method calculates the distance between two Vec3 objects, by following the distance formula in euclidean space. Notice that this distance() method takes two Vec3 objects as input, self and other.
The self argument corresponds to the Vec3 object from which this distance() method is being called from. While the other is a separate Vec3 object that is given as input to this method. In the example below, the self argument corresponds to the object v1, because the distance() method is being called from the v1 object, while the other argument corresponds to the object v2.
But what if we do have a method that alters the state of the object, by altering the values of its data members, how should we annotate self in this instance? The answer is: “we should annotate self as a pointer of x, instead of just x”. In other words, you should annotate self as self: *x, instead of annotating it as self: x.
If we create a new method inside the Vec3 object that, for example, expands the vector by multiplying its coordinates by a factor of two, then, we need to follow this rule specified in the previous paragraph. The code example below demonstrates this idea:
Notice in the code example above that we have added a new method to our Vec3 struct named twice(). This method doubles the coordinate values of our vector object. In the case of the twice() method, we annotated the self argument as *Vec3, indicating that this argument receives a pointer (or a reference, if you prefer to call it this way) to a Vec3 object as input.
Now, if you change the self argument in this twice() method to self: Vec3, like in the distance() method, you will get the compiler error exposed below as result. Notice that this error message is showing a line from the twice() method body, indicating that you cannot alter the value of the x data member.
-
// If we change the function signature of double to:
-pubfn twice(self: Vec3) void{
+
// If we change the function signature of double to:
+pubfn twice(self: Vec3) void{
Because these weird values contain a dot character before them, we are asking the zig compiler to infer the types of these values inside the switch statement. Then, the zig compiler is looking into the current context where these values are being used, and it is trying to infer the types of these values.
Since they are being used inside a switch statement, the zig compiler looks into the type of the input object given to the switch statement, which is the order object in this case. Because this object have type AtomicOrder, the zig compiler infers that these values are data members from this type AtomicOrder.
This is how basic type inference is done in Zig. If we didn’t use the dot character before the values inside this switch statement, then, we would be forced to explicitly write the data types of these values. For example, instead of writing .release we would have to write AtomicOrder.release. We would have to do this for every single value in this switch statement, and this is a lot of work. That is why type inference is commonly used on switch statements in Zig.
@@ -918,60 +922,71 @@
Most languages have a formal way to perform type casting. In Rust for example, we normally use the keyword as, and in C, we normally use the type casting syntax, e.g. (int) x. In Zig, we use the @as() built-in function to cast an object of type “x”, into an object of type “y”.
This @as() function is the preferred way to perform type conversion (or type casting) in Zig. Because it is explicit, and, it also performs the casting only if it is unambiguous and safe. To use this function, you just provide the target data type in the first argument, and, the object that you want cast as the second argument.
1/1 filefe9b6a29a64b.test_0...OKAll 1 tests passed
+ d.
+
This is the general way to perform type casting in Zig. But remember, @as() works only when casting is unambiguous and safe, and there are situations where these assumptions do not hold. For example, when casting an integer value into a float value, or vice-versa, it is not clear to the compiler how to perform this conversion safely.
Therefore, we need to use specialized “casting functions” in such situations. For example, if you want to cast an integer value into a float value, then, you should use the @floatFromInt() function. In the inverse scenario, you should use the @intFromFloat() function.
In these functions, you just provide the object that you want to cast as input. Then, the target data type of the “type casting operation” is determined by the type annotation of the object where you are saving the results. In the example below, we are casting the object x into a value of type f32, because the object y, which is where we are saving the results, is annotated as an object of type f32.
1/1 filefe9b126af3c3.test_0...OKAll 1 tests passed
+ d.
+
Another built-in function that is very useful when performing type casting operations is @ptrCast(). In essence, we use the @as() built-in function when we want to explicit convert (or cast) a Zig value/object from a type “x” to a type “y”, etc. However, pointers (we are going to discuss pointers in more depth in Chapter 6) are a special type of object in Zig, i.e. they are treated differently from “normal objects”.
Everytime a pointer is involved in some “type casting operation” in Zig, the @ptrCast() function is used. This function works similarly to @floatFromInt(). You just provide the pointer object that you want to cast as input to this function, and the target data type is, once again, determined by the type annotation of the object where the results are being stored.
We already talked about what modules are, and also, how to import other modules into your current module via import statements. Every Zig module (i.e. a .zig file) that you write in your project is internally stored as a struct object. Take the line exposed below as an example. In this line we are importing the Zig Standard Library into our current module.
-
const std = @import("std");
+
const std = @import("std");
When we want to access the functions and objects from the standard library, we are basically accessing the data members of the struct stored in the std object. That is why we use the same syntax that we use in normal structs, with the dot operator (.) to access the data members and methods of the struct.
When this “import statement” get’s executed, the result of this expression is a struct object that contains the Zig Standard Library modules, global variables, functions, etc. And this struct object get’s saved (or stored) inside the constant object named std.
Take the thread_pool.zig module from the project zap3 as an example. This module is written as if it was a big struct. That is why we have a top-level and public init() method written in this module. The idea is that all top-level functions written in this module are methods from the struct, and all top-level objects and struct declarations are data members of this struct. The module is the struct itself.
So you would import and use this module by doing something like this:
-
const std = @import("std");
-const ThreadPool = @import("thread_pool.zig");
-const num_cpus = std.Thread.getCpuCount()
-catch@panic("failed to get cpu core count");
-const num_threads = std.math.cast(u16, num_cpus)
-catch std.math.maxInt(u16);
-const pool = ThreadPool.init(
- .{ .max_threads = num_threads }
-);
+
const std = @import("std");
+const ThreadPool = @import("thread_pool.zig");
+const num_cpus = std.Thread.getCpuCount()
+catch@panic("failed to get cpu core count");
+const num_threads = std.math.cast(u16, num_cpus)
+catch std.math.maxInt(u16);
+const pool = ThreadPool.init(
+ .{ .max_threads = num_threads }
+);
You can have multiple test blocks written on the same Zig module. Also, you can mix test blocks with your source code, with no problems or consequences. If you mix test blocks with your normal source code, when you execute the build, build-exe, build-obj or build-lib commands from the zig compiler that we exposed at Section 1.2.4, these test blocks are automatically ignored by the compiler.
In other words, the zig compiler builds and execute your unit tests only when you ask it to. By default, the compiler always ignore test blocks written in your Zig modules. The compiler normally checks only if there are any syntax errors in these test blocks.
@@ -331,7 +335,7 @@
8.2 How to run your tests
If the zig compiler ignores any test block by default, how can you compile and run your unit tests? The answer is the test command from the zig compiler. By running the zig test command, the compiler will find every instance of a test block in your Zig modules, and, it will compile and run the unit tests that you wrote.
-
zig test simple_sum.zig
+
zig test simple_sum.zig
1/1 simple_sum.test.testing simple sum... OK
All 1 tests passed.
The code example below demonstrates such type of unit test in Zig. Notice that, inside the function alloc_error() we are allocating 100 bytes of memory, or, an array of 100 elements, for the object ibuffer. However, in the test block, we are using the FixedBufferAllocator() allocator object, which is limited to 10 bytes of space, because the object buffer, which we provided to the allocator object, have only 10 bytes of space.
That is why, the alloc_error() function raises an OutOfMemory error on this case. Because this function is trying to allocate more space than the allocator object allows. So, in essence, we are testing for a specific type of error, which is OutOfMemory. If the alloc_error() function returns any other type of error, then, the expectError() function would make the entire test fail.
1/1 file103bc78cf09de.test.arrays are equal?...OKA
+ All 1 tests passed.
+
1/1 oom.test.arrays are equal?... OK
All 1 tests passed.
At last, you might also want to use the expectEqualStrings() function. As the name suggests, you can use this function to test if two strings are equal or not. Just provide the two string objects that you want to compare, as inputs to the function.
If the function finds any existing differences between the two strings, then, the function will raise an error, and also, print an error message that shows the exact difference between the two string objects provided, as the example below demonstrates:
This syntax to dereference the pointer is nice. Because we can easily chain it with methods of the value pointed by the pointer. We can use the User struct that we have created at Section 2.3 as an example. If you comeback to that section, you will see that this struct have a method named print_name().
So, for example, if we have an user object, and a pointer that points to this user object, we can use the pointer to access this user object, and, at the same time, call the method print_name() on it, by chaining the dereference method (*) with the print_name() method. Like in the example below:
We can also use pointers to effectively alter the value of an object. For example, I could use the pointer object to set the value of the object number to 6, like in the example below.
You can have a pointer that points to a constant object, or, a pointer that points to a variable object. But regardless of who this pointer is, a pointer must always respect the characteristics of the object that it points to. As a consequence, if the pointer points to a constant object, then, you cannot use this pointer to change the value that it points to. Because it points to a value that is constant. As we discussed at Section 1.4, you cannot change a value that is constant.
For example, if I have a number object, which is constant, I cannot execute the expression below where I’m trying to change the value of number to 6 through the pointer object. As demonstrated below, when you try to do something like that, you get a compile time error:
p.zig:6:12: error: cannot assign to constant
pointer.* = 6;
If I change the number object to be a variable object, by introducing the var keyword, then, I can successfully change the value of this object through a pointer, as demonstrated below:
Until this point, the pointer object was always constant, but what this means for us? What is the consequence of the pointer object being constant? The consequence is that we cannot change the pointer object, because it is constant. We can use the pointer object in multiple ways, but we cannot change the memory address that is inside this pointer object.
However, if we mark the pointer object as a variable object, then, we can change the memory address pointed by this pointer object. The example below demonstrates that. Notice that the object pointed by the pointer object changes from c1 to c2.
Pointer arithmetic is available in Zig, and they work the same way they work in C. When you have a pointer that points to an array, the pointer usually points to the first element in the array, and you can use pointer arithmetic to advance this pointer and access the other elements in the array.
Notice in the example below, that initially, the ptr object was pointing to the first element in the array ar. But then, I started to walk through the array, by advancing the pointer with simple pointer arithmetic.
Behind the hood, slices already are pointers, and they also come with the len property, which indicates how many elements are in the slice. This is good because the zig compiler can use it to check for potential buffer overflows, and other problems like that.
Also, you don’t need to use pointer arithmetic to walk through the elements of a slice. You can simply use the slice[index] syntax to directly access any element you want in the slice. As I mentioned at Section 1.6, you can get a slice from an array by using a range selector inside brackets. In the example below, I’m creating a slice (sl) that covers the entire ar array. I can access any element of ar from this slice, and, the slice itself already is a pointer behind the hood.
p5.zig:5:14: error: expected type 'u8',
found '@TypeOf(null)'
@@ -465,8 +468,8 @@
An optional object in Zig is an object that can be null. To mark an object as optional, we use the ? operator. When you put this ? operator right before the data type of an object, you transform this data type into an optional data type, and the object becomes an optional object.
Take the snippet below as an example. We are creating a new variable object called num. This object have the data type ?i32, which means that, this object contains either a signed 32-bit integer (i32), or, a null value. Both alternatives are valid values to the num object. That is why, I can actually change the value of this object to null, and, no errors are raised by the zig compiler, as demonstrated below:
-
var num: ?i32 = 5;
-num = null;
+
var num: ?i32 = 5;
+num = null;
@@ -475,18 +478,18 @@
In the example below, we are creating a variable object named num, and an optional pointer object named ptr. Notice that the data type of the object ptr indicates that it is either a null value, or a pointer to an i32 value. Also, notice that the pointer object (ptr) can be marked as optional, even if the object num is not optional.
What this code tells us is that, the num variable will never contain a null value. This variable will always contain a valid i32 value. But in contrast, the ptr object might contain either a null value, or, a pointer to an i32 value.
But what happens if we turn the table, and mark the num object as optional, instead of the pointer object. If we do that, then, the pointer object is not optional anymore. It would be a similar (although different) result. Because then, we would have a pointer to an optional value. In other words, a pointer to a value that is either a null value, or, a not-null value.
In the example below, we are recreating this idea. Now, the ptr object have a data type of *?i32, instead of ?*i32. Notice that the * symbol comes before of ? this time. So now, we have a pointer that points to a value that is either null , or, a signed 32-bit integer.
-
var num: ?i32 = 5;
-// ptr have type `*?i32`, instead of `?*i32`.
-const ptr = #
-_ = ptr;
+
var num: ?i32 = 5;
+// ptr have type `*?i32`, instead of `?*i32`.
+const ptr = #
+_ = ptr;
@@ -500,10 +503,10 @@
When you use an if statement, you use a pair of pipes to unwrap the optional value, and use this “unwrapped object” inside the if block. Using the example below as a reference, if the object num is null, then, the code inside the if statement is not executed. Otherwise, the if statement will unwrap the object num into the not_null_num object. This not_null_num object is guaranteed to be not null inside the scope of the if statement.
The idea behind the orelse keyword is: if the expression on the left side result in a not-null value, then, this not-null value is used. However, if this expression on the left side result in a null value, then, the value of the expression on the right side is used instead.
Looking at the example below, since the x object is currently null, the orelse decided to use the alternative value, which is the number 15.
You can use the if statement or the orelse keyword, when you want to solve (or deal with) this null value. However, if there is no clear solution to this null value, and the most logic and sane path is to simply panic and raise a loud error in your program when this null value is encountered, you can use the ? method of your optional object.
In essence, when you use this ? method, the optional object is unwrapped. If a not-null value is found in the optional object, then, this not-null value is used. Otherwise, the unreachable keyword is used. You can read more about this unreacheable keyword at the official documentation1. But in essence, when you build your Zig source code using the build modes ReleaseSafe or Debug, this unreacheable keyword causes the program to panic and raise an error during runtime, like in the example below:
thread 12767 panic: attempt to use null value
p7.zig:12:34: 0x103419d in main (p7):
diff --git a/docs/Chapters/09-data-structures.html b/docs/Chapters/09-data-structures.html
index 75cbf5a3..c556478d 100644
--- a/docs/Chapters/09-data-structures.html
+++ b/docs/Chapters/09-data-structures.html
@@ -599,6 +599,10 @@
This means that, with swapRemove() you remove the value from the hashtable, but you do not preserve the order in which the values were inserted into the structure. While orderedRemove() is able to retain the order in which these values were inserted.
But instead of providing an index as input to swapRemove() or orderedRemove(), like I described at Section 11.1.4, these methods here in an ArrayHashMap take a key as input, like the remove() method from a HashMap object. If you want to provide an index as input, instead of a key, you should use the swapRemoveAt() and orderedRemoveAt() methods.
-
var hash_table = AutoArrayHashMap(u32, u16)
- .init(allocator);
-defer hash_table.deinit();
+
var hash_table = AutoArrayHashMap(u32, u16)
+ .init(allocator);
+defer hash_table.deinit();
@@ -640,33 +644,33 @@
<
One thing that you will notice in the other two types of hashtables that I have presented over the last sections, is that neither of them accepts a slice data type in their keys. What this means is that you cannot use a slice value to represent a key in these types of hashtable.
The most obvious consequence of this, is that you cannot use strings as keys in these hashtables. But it is extremely common to use strings as keys in hashtables.
Take this very simple Javascript code snippet as an example. We are creating a simple hashtable object named people. Then, we add a new entry to this hashtable, which is identified by the string 'Pedro'. This string is the key in this case, while the object containing different personal information such as age, height and city, is the value to be stored in the hashtable.
-
var people =newObject();
-people['Pedro'] = {
-'age':25,
-'height':1.67,
-'city':'Belo Horizonte'
-};
+
var people =newObject();
+people['Pedro'] = {
+'age':25,
+'height':1.67,
+'city':'Belo Horizonte'
+};
This pattern of using strings as keys is very common in all sorts of situations. That is why the Zig Standard Library offers a specific type of hashtable for this purpose, which is created through the StringHashMap() function. This function creates a hashtable that uses strings as keys. The only input of this function is the data type of the values that will be stored into this hashtable.
In the example below, I’m creating a hashtable to store the ages of different people. Each key in this hashtable is represented by the name of each person, while the value stored in the hashtable is the age of this person identified by the key.
That is why I provide the u8 data type (which is the data type used by the age values) as input to this StringHashMap() function. As the result, it creates a hashtable that uses string values as keys, and, that stores u8 values in it. Notice that an allocator object is provided at the init() method of the resulting object from the StringHashMap() function.
Section 11.2.5. And you can also get values from the hashtable by using the same get() method. Like its ArrayHashMap brother, to delete values from this specific type of hashtable, we also use the orderedRemove() and swapRemove() methods, with the same effects that I have described at Section 11.2.4.
If we take the code example that was exposed at Section 11.2.5, we can achieve the exact same result with StringArrayHashMap():
-
var ages = std.StringArrayHashMap(u8).init(allocator);
+
var ages = std.StringArrayHashMap(u8).init(allocator);
@@ -722,24 +726,24 @@
Thus, if we have used a doubly linked list, we can use the insertBefore() method to store the pointer to the input node in the prev attribute. This would put the input node as the “previous node”, or, the node before the current node. In contrast, the insertAfter() method puts the pointer created to the input node in the next attribute of the current node, and as result, the input node becomes the “next node” of the current node.
Since we are using a singly linked list in this example, we have only the insertAfter() method available in the node objects that we create from our Lu32 type.
There are other methods available from the linked list object, depending if this object is a singly linked list or a doubly linked list, that might be very useful for you. You can find a summary of them in the bullet points below:
@@ -756,30 +760,30 @@
<
Zig introduces a new data structure called MultiArrayList(). It is a different version of the dynamic array that we have introduced at Section 11.1. The difference between this structure and the ArrayList() that we know from Section 11.1, is that MultiArrayList() creates a separate dynamic array for each field of the struct that you provide as input.
Consider the following code example. We create a new custom struct called Person. This struct contains three different data members, or, three different fields. As consequence, when we provide this Person data type as input to MultiArrayList(), this creates a “struct of three different arrays” called PersonArray. In other words, this PersonArray is a struct that contains three internal dynamic arrays in it. One array for each field found in the Person struct definition.
In other words, instead of creating an array of “persons”, the MultiArrayList() function creates a “struct of arrays”. Each data member of this struct is a different array that stores the values of a specific field from the Person values that were added (or, appended) to this “struct of arrays”. One important detail is that each of these separate internal arrays stored inside PersonArray are dynamic arrays. This means that these arrays can grow in capacity automatically as needed, to accomodate more values.
The Figure 11.5 exposed below presents a diagram that describes the PersonArray struct that we have created in the previous code example. Notice that the values of the data members present in each of the three Person values that we have appended into the PersonArray object , are scattered across three different internal arrays of the PersonArray object.
@@ -795,9 +799,9 @@
<
You can easily access each of these arrays separately, and iterate over the values of each array. For that, you will need to call the items() method from the PersonArray object, and provide as input to this method, the name of the field that you want to iterate over. If you want to iterate through the .age array for example, then, you need to call items(.age) from the PersonArray object, like in the example below:
-
for (people.items(.age)) |*age| {
-try stdout.print("Age: {d}\n", .{age.*});
-}
+
for (people.items(.age)) |*age| {
+try stdout.print("Age: {d}\n", .{age.*});
+}
Age: 15
Age: 26
@@ -806,15 +810,15 @@
<
In this example we are calling the items() method directly from the PersonArray object. However, it is recommended on most situations to call this items() method from a “slice object”, which you can create from the slice() method. The reason for this is that calling items() multiple times have better performance if you use a slice object.
Therefore, if you are planning to access only one of the internal arrays from your “multi array struct”, it is fine to call items() directly from the multi array object. But if you need to access many of the internal arrays from your “multi array struct”, then, you will likely need to call items() more than once, and, in such circumstance, is better to call items() through a slice object. The example below demonstrates the use of such object:
1/1 file10459ffd0e21.test.coerce error value...OKA
+ All 1 tests passed.
+
@@ -495,14 +499,14 @@
Look at the code example below. Once again, we go back to the previous example where we were trying to open a file that doesn’t exist in my computer, but this time, I use catch to actually implement a logic to handle the error, instead of just stopping the execution right away.
More specifically, in this example, I’m using a logger object to record some logs into the system, before I return the error, and stop the execution of the program. For example, this could be some part of the codebase of a complex system that I do not have full control over, and I want to record these logs before the program crashes, so that I can debug it later (e.g. maybe I cannot compile the full program, and properly debug it with a debugger. So, these logs might be a valid strategy to surpass this barrier).
Therefore, we use catch to create a block of expressions that will handle the error. I can return the error value from this block of expressions, like I did in the above example, which, will make the program enter in panic mode, and, stop the execution. But I could also, return a valid value from this block of code, which would be stored in the file object.
Notice that, instead of writing the keyword before the expression that might return the error, like we do with try, we write catch after the expression. We can open the pair of pipes (|), which captures the error value returned by the expression, and makes this error value available in the scope of the catch block as the object named err. In other words, because I wrote |err| in the code, I can access the error value returned by the expression, by using the err object.
@@ -510,7 +514,7 @@
The Zig official language reference, provides a great example of this “default value” strategy with catch. This example is reproduced below. Notice that we are trying to parse some unsigned integer from a string object named str. In other words, this function is trying to transform an object of type []const u8 (i.e. an array of characters, a string, etc.) into an object of type u64.
But this parsing process done by the function parseU64() may fail, resulting in a runtime error. The catch keyword used in this example provides an alternative value (13) to be used in case this parseU64() function raises an error. So, the expression below essentially means: “Hey! Please, parse this string into a u64 for me, and store the results into the object number. But, if an error occurs, then, use the value 13 instead”.
-
const number = parseU64(str, 10) catch13;
+
const number = parseU64(str, 10) catch13;
So, at the end of this process, the object number will contain either a u64 integer that was parsed successfully from the input string str, or, if an error occurs in the parsing process, it will contain the u64 value 13 that was provided by the catch keyword as the “default”, or, the “alternative” value.
@@ -521,30 +525,30 @@
<
This means that, if the parseU64() expression returns a valid value, this value becomes available inside the scope of this “if branch” (i.e. the “true branch”) through the object that we listed inside the pair of pipe character (|), which is the object number.
If an error occurs, we can use an “else branch” (or the “false branch”) of the if statement to handle the error. In the example below, we are using the else in the if statement to unwrap the error value (that was returned by parseU64()) into the err object, and handle the error.
-
if (parseU64(str, 10)) |number| {
-// do something with `number` here
-}else |err| {
-// handle the error value.
-}
+
if (parseU64(str, 10)) |number| {
+// do something with `number` here
+}else |err| {
+// handle the error value.
+}
Now, if the expression that you are executing returns different types of error values, and you want to take a different action in each of these types of error values, the try and catch keywords, and the if statement strategy, becomes limited.
For this type of situation, the official documentation of the language suggests the use of a switch statement together with an if statement (Zig Software Foundation 2024b). The basic idea is, to use the if statement to execute the expression, and use the “else branch” to pass the error value to a switch statement, where you define a different action for each type of error value that might be returned by the expression executed in the if statement.
The example below demonstrates this idea. We first try to add (or register) a set of tasks to a queue. If this “registration process” occurs well, we then try to distribute these tasks across the workers of our system. But if this “registration process” returns an error value, we then use a switch statement in the “else branch” to handle each possible error value.
-
if (add_tasks_to_queue(&queue, tasks)) |_| {
- distribute_tasks(&queue);
-}else |err| switch (err) {
-error.InvalidTaskName => {
-// do something
-},
-error.TimeoutTooBig => {
-// do something
-},
-error.QueueNotFound => {
-// do something
-},
-// and all the other error options ...
-}
+
if (add_tasks_to_queue(&queue, tasks)) |_| {
+ distribute_tasks(&queue);
+}else |err| switch (err) {
+error.InvalidTaskName => {
+// do something
+},
+error.TimeoutTooBig => {
+// do something
+},
+error.QueueNotFound => {
+// do something
+},
+// and all the other error options ...
+}
@@ -554,14 +558,14 @@
The basic idea is to provide an expression to the errdefer keyword. Then, errdefer executes this expression if, and only if, an error occurs during the execution of the current scope. In the example below, we are using an allocator object (that we have presented at Section 3.3) to create a new User object. If we are successful in creating and registering this new user, this create_user() function will return this new User object as its return value.
However, if for some reason, an error value is generated by some expression that is after the errdefer line, for example, in the db.add(user) expression, the expression registered by errdefer get’s executed before the error value is returned from the function, and before the program enters in panic mode and stops the current execution.
-
fn create_user(db: Database, allocator: Allocator) !User {
-const user = try allocator.create(User);
-errdefer allocator.destroy(user);
-
-// Register new user in the Database.
- _ = try db.register_user(user);
-return user;
-}
+
fn create_user(db: Database, allocator: Allocator) !User {
+const user = try allocator.create(User);
+errdefer allocator.destroy(user);
+
+// Register new user in the Database.
+ _ = try db.register_user(user);
+return user;
+}
By using errdefer to destroy the user object that we have just created, we guarantee that the memory allocated for this user object get’s freed, before the execution of the program stops. Because if the expression try db.add(user) returns an error value, the execution of our program stops, and we lose all references and control over the memory that we have allocated for the user object. As a result, if we do not free the memory associated with the user object before the program stops, we cannot free this memory anymore. We simply lose our chance to do the right thing. That is why errdefer is essential in this situation.
Just to state clearly the differences between defer and errdefer (which I described at Section 2.1.3 and Section 2.1.4), it might be worth to discuss the subject a bit further. You might still have the question “why use errdefer if we can use defer instead?” in your mind.
@@ -581,30 +585,30 @@
The union LakeTarget defined below allows the lake_target argument of send_event() to be either an object of type AzureBlob, or type AmazonS3, or type GoogleGCP. This union allows the send_event() function to receive an object of any of these three types as input in the lake_target argument.
Remember that each of these three types (AmazonS3, GoogleGCP and AzureBlob) are separate structs that we have defined in our source code. So, at first glance, they are separate data types in our source code. But is the union keyword that unifies them into a single data type called LakeTarget.
-
const LakeTarget = union{
- azure: AzureBlob,
- amazon: AmazonS3,
- google: GoogleGCP,
-};
-
-fn send_event(
- event: Event,
- lake_target: LakeTarget
-) bool{
-// body of the function ...
-}
+
const LakeTarget = union{
+ azure: AzureBlob,
+ amazon: AmazonS3,
+ google: GoogleGCP,
+};
+
+fn send_event(
+ event: Event,
+ lake_target: LakeTarget
+) bool{
+// body of the function ...
+}
An union definition is composed by a list of data members. Each data member is of a specific data type. In the example above, the LakeTarget union have three data members (azure, amazon, google). When you instantiate an object that uses an union type, you can only use one of its data members in this instantiation.
You could also interpret this as: only one data member of an union type can be activated at a time, the other data members remain deactivated and unaccessible. For example, if you create a LakeTarget object that uses the azure data member, you can no longer use or access the data members google or amazon. It is like if these other data members didn’t exist at all in the LakeTarget type.
You can see this logic in the example below. Notice that, we first instantiate the union object using the azure data member. As a result, this target object contains only the azure data member inside of it. Only this data member is active in this object. That is why the last line in this code example is invalid. Because we are trying to instantiate the data member google, which is currently inactive for this target object, and as a result, the program enters in panic mode warning us about this mistake through a loud error message.
-
var target = LakeTarget {
- .azure = AzureBlob.init()
-};
-// Only the `azure` data member exist inside
-// the `target` object, and, as a result, this
-// line below is invalid:
-target.google = GoogleGCP.init();
+
var target = LakeTarget {
+ .azure = AzureBlob.init()
+};
+// Only the `azure` data member exist inside
+// the `target` object, and, as a result, this
+// line below is invalid:
+target.google = GoogleGCP.init();
thread 2177312 panic: access of union field 'google' while
field 'azure' is active:
@@ -613,21 +617,21 @@
So, when you instantiate an union object, you must choose one of the data types (or, one of the data members) listed in the union type. In the example above, I choose to use the azure data member, and, as a result, all other data members were automatically deactivated, and you can no longer use them after you instantiate the object.
You can activate another data member by completely redefining the entire enum object. In the example below, I initially use the azure data member. But then, I redefine the target object to use a new LakeTarget object, which uses the google data member.
A curious fact about union types, is that, at first, you cannot use them in switch statements (which were presented at Section 2.1.2). In other words, if you have an object of type LakeTarget for example, you cannot give this object as input to a switch statement.
But what if you really need to do so? What if you actually need to provide an “union object” to a switch statement? The answer to this question relies on another special type in Zig, which are the tagged unions. To create a tagged union, all you have to do is to add an enum type into your union declaration.
As an example of a tagged union in Zig, take the Registry type exposed below. This type comes from the grammar.zig module2 from the Zig repository. This union type lists different types of registries. But notice this time, the use of (enum) after the union keyword. This is what makes this union type a tagged union. By being a tagged union, an object of this Registry type can be used as input in a switch statement. This is all you have to do. Just add (enum) to your union declaration, and you can use it in switch statements.
But what if we provide a number that is not compile-time known to this function? For example, we might provide a different input value to this function depending on the target OS of our compilation process. The code example below demonstrates such case.
Because the value of the object n is determined at runtime, we cannot provide this object as input to the twice() function. The zig compiler will not allow it, because we marked the num argument as a “comptime argument”. That is why the zig compiler raises the compile-time error exposed below:
t.zig:12:16: error: runtime-known argument passed to comptime parameter
Comptime arguments are frequently used on functions that return some sort of generic structure. In fact, comptime is the essence (or the basis) to make generics in Zig. We are going to talk more about generics at Section 12.2.
For now, let’s take a look at this code example from Seguin (2024). You can see that this IntArray() function have one argument named length. This argument is marked as comptime, and receives a value of type usize as input. So the value given to this argument must be compile-time known. We can also see that this function returns an array of i64 values as output.
Now, the key component of this function is the length argument. This argument is used to determine the size of the array that is produced by the function. Let’s think about the consequences of that. If the size of the array is dependent on the value assigned to the length argument, this means that the data type of the output of the function depends on the value of this length argument.
Let this statement sink for a bit in your mind. As I described at Section 1.2.2, Zig is a strongly-typed language, especially on function declarations. So every time we write a function in Zig, we have to annotate the data type of the value returned by the function. But how can we do that, if this data type depends on the value given to the argument of the function?
. We are executing the same fibonacci() function both at runtime, and, at compile-time. The function is by default executed at runtime, but because we use the comptime keyword at the second “try expression”, this expression is executed at compile-time.
This might be a bit confusing for some people. Yes! When I say that this expression is executed at compile-time, I mean that this expression is compiled and executed while the zig compiler is compiling your Zig source code.
1/1 file10548bb87337.test.fibonacci...OKAll 1 test
+ ts passed.
+
A lot of your Zig source code might be potentially executed at compile-time, because the zig compiler can figure it out the output of some expressions. Especially if these expressions depends only at compile-time known values. We have talked about this at Section 3.1.1.
But when you use the comptime keyword on an expression, there is no “it might be executed at compile-time” anymore. With the comptime keyword you are ordering the zig compiler to execute this expression at compile-time. You are imposing this rule, it is guaranteed that the compiler will always execute it at compile-time. Or, at least, the compiler will try to execute it. If the compiler cannot execute the expression for whatever reason, the compiler will raise a compilation error.
@@ -430,22 +438,26 @@
Blocks were described at Section 1.7. When you apply the comptime keyword over a block of expressions, you get essentially the same effect when you apply this keyword to a single expression. That is, the entire block of expressions is executed at compile-time by the zig compiler.
In the example below, we mark the block labeled of blk as a comptime block, and, therefore, the expressions inside this block are executed at compile-time.
1/1 file105482dc0b880.test.fibonacci in a block...
+ .OKAll 1 tests passed.
+
@@ -460,23 +472,30 @@
Because we have used this type keyword in the T argument, we are telling the zig compiler that this T argument will receive some data type as input. Also notice the use of the comptime keyword in this argument. As I described at Section 12.1, every time you use this keyword in a function argument, this means that the value of this argument must be known at compile-time. This makes sense, right? Because there is no data type that is not known at compile-time.
Think about this. Every data type that you will ever write is always known at compile-time. Especially because data types are an essential information for the compiler to actually compile your source code. Having this in mind, makes sense to mark this argument as a comptime argument.
-
fn max(comptime T: type, a: T, b: T) T {
-returnif (a > b) a else b;
-}
+
fn max(comptime T: type, a: T, b: T) T {
+returnif (a > b) a else b;
+}
+
+
All 0 tests passed.
+
Also notice that the value of the T argument is actually used to define the data type of the other arguments in the function, a and b, and also at the return type annotation of the function. That is, the data type of these arguments (a and b), and, the return data type of the function itself, are determined by the input value given to the T argument.
As a result, we have a generic function that works with different data types. For example, I can provide u8 values to this max() function, and it will work as expected. But if I provide f64 values instead, it will also work as expected. Without a generic function, I would have to write a different max() function for each one of the data types that I wanted to use. This generic function provides a very useful shortcut for us.
After we make sure that we have enough room to store this new value that we are adding to the stack, all we have to do is to assign this value to the top element in this stack, and, increase the value of the length attribute by one. We find the top element in the stack by using the length attribute.
We do that by using a generic function. Because a generic function can receive a data type as input, and we can pass this data type to the struct definition of our Stack object. Therefore, we can use the generic function to create a Stack object that can store the data type we want. If we want to create a stack structure that stores User values, we pass the User data type to this generic function, and it will create for us the struct definition that describes a Stack object that can store User values in it.
Look at the code example below. I have omitted some parts of the Stack struct definition for brevity reasons. However, if a specific part of our Stack struct is not exposed here in this example, then it is because this part did not change from the previous example. It remains the same.
Notice that we have created a function in this example named Stack(). This function takes a type as input, and passes this type to the struct definition of our Stack object. The data member items is now, an array of type T, which is the data type that we have provided as input to the function. The function argument val in the push() function is now a value of type T too.
We can just provide a data type to this function, and it will create a definition of a Stack object that can store values of the data type that we have provided. In the example below, we are creating the definition of a Stack object that can store u8 values in it. This definition is stored at the Stacku8 object. This Stacku8 object becomes our new struct, it is the struct that we are going to use to create our Stack object.
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.
In Zig, we don’t need to use a separate set of tools to build our projects, because a build system is embedded inside the language itself. Therefore, 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.
+
The second generated file (build.zig.zon) is a JSON-like file, in which you can describe your project, and also, declare a set of dependencies of your project that you want to fetch from the internet. In other words, you can use this build.zig.zon file to include a list of external libraries in your project.
+
One possible way to include an external Zig library in your project, is to manually build and install the library in your system, and just link your source code with the library at the build step of your project.
+
However, if this external Zig library is available on GitHub for example, and it has a valid build.zig.zon file in root folder of the project, which describes the project, you can easily include this library in your project by simply listing this external library in your build.zig.zon file.
+
In other words, this build.zig.zon file works similarly to the package.json file in Javascript projects, or the Pipfile file in Python projects, or the Cargo.toml file in Rust projects. You can read more about this specific file in a couple of articles on the internet23, and you can also see the expected schema for this build.zig.zon file in a documentation file inside the official repository of Zig4.
1.2.2 The file root.zig
-
Let’s take a look into the root.zig file. You might have noticed that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language2.
+
Let’s take a look into the root.zig file. You might have noticed that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language5.
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. 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.
@@ -430,14 +433,15 @@
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 there are some other elements that we haven’t 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. The return type of the function (void) is accompanied by an exclamation mark (!). This exclamation mark tells us that this main() function might return an error.
-
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:
+
Is worth noting that, a main() function in Zig is allowed to return nothing (void), or an unsigned 8-bit integer (u8) value, or an error. In other words, you can write your main() function in Zig to return essentially nothing (void), or, if you prefer, you can also write a more C-like main() function, which returns an integer value that usually serves as a “status code” for the process.
+
In this example, the return type annotation of main() indicates that this function can either return nothing (void), or return an error. This exclamation mark in the return type annotation is an interesting and powerful feature of Zig. In summary, if you write a function and something inside the body of this function might return an error, then, you are forced to:
-
either add the exclamation mark to the return type of the function and make it clear that this function might return an error
-
explicitly handle this error inside the function
+
either add the exclamation mark to the return type of the function and make it clear that this function might return an error.
+
explicitly handle this error inside the function.
In most programming languages, we normally handle (or deal with) an error through a try catch pattern. Zig does have both try and catch keywords. But they work a little differently than what you’re probably used to in other languages.
If we look at the main() function below, you can see that we do have a try keyword on the 5th line. But we do not have a catch keyword in this code. In Zig, we use the try keyword to execute an expression that might return an error, which, in this example, is the stdout.print() expression.
-
In essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword does nothing. It only passes the value forward. But if the expression does return an error, then, the try keyword just unwrap the error value, and return this error from the function and also prints the current stack trace to stderr.
+
In essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword does absolutely nothing. It only passes the value forward. It is like if this try keyword was never there. However, if the expression does return an error, then, the try keyword will unwrap the error value, then, it returns this error from the function and also prints the current stack trace to stderr.
This 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 stop even if you don’t want to stop the execution. You are obligated to face the error.
const std = @import("std");
@@ -447,9 +451,8 @@
try stdout.print("Hello, {s}!\n", .{"world"});}
-
Another thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. It marks the main() function as a public function from this module.
-
Every function in your Zig module is by default private to this Zig module and can only be called from within the module. Unless, you explicitly mark this function as a public function with the pub keyword. This means that the pub keyword in Zig does essentially the opposite of what the static keyword do in C/C++.
-
By making a function “public” you allow other Zig modules to access and call it. A calling Zig module imports the module with the @import() built-in. That makes all public functions from the imported module visible.
+
Another thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. It marks the main() function as a public function from this module. Every function in your Zig module is by default private to this Zig module and can only be called from within the module. Unless, you explicitly mark this function as a public function with the pub keyword.
+
If you think about it, this pub keyword in Zig does essentially the opposite of what the static keyword do in C/C++. By making a function “public” you allow other Zig modules to access and call this function. A calling Zig module imports another module by using the @import() built-in function, which makes all public functions from the imported module visible to the calling Zig module.
1.2.4 Compiling your source code
@@ -473,7 +476,7 @@
<
1.2.6 Important note for Windows users
-
First of all, this is a Windows-specific thing, and, therefore, does not apply to other operational systems, such as Linux and MacOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.
+
First of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.
An example of that is accessing the stdout (i.e. the standard output of your system), which is usually done in Zig by using the expression std.io.getStdOut(). Thus, if you use this expression to instantiate a global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows, with a “unable to evaluate comptime expression” error message.
This failure in the compilation process happens because all global variables in Zig are initialized at compile-time. However, operations like accessing the stdout (or opening a file) on Windows depends on resources that are available only at runtime (you will learn more about compile-time versus runtime at Section 3.1.1).
For example, if you try to compile this code example on Windows, you will likely get the error message exposed below:
@@ -501,7 +504,7 @@
}
Hello
-
You can read more details about this Windows-specific limitation at a couple of GitHub issues opened at the official Zig repository. More specifically, the issues 17186 3 and 19864 4.
+
You can read more details about this Windows-specific limitation at a couple of GitHub issues opened at the official Zig repository. More specifically, the issues 17186 6 and 19864 7.
Now, one of the best ways to learn Zig is to simply read Zig code. Try to read Zig code often, and things will become more clear. A C/C++ programmer would also probably give you this same tip. Because this strategy really works!
-
Now, where can you find Zig code to read? I personally think that, the best way of reading Zig code is to read the source code of the Zig Standard Library. The Zig Standard Library is available at the lib/std folder5 on the official GitHub repository of Zig. Access this folder, and start exploring the Zig modules.
+
Now, where can you find Zig code to read? I personally think that, the best way of reading Zig code is to read the source code of the Zig Standard Library. The Zig Standard Library is available at the lib/std folder8 on the official GitHub repository of Zig. Access this folder, and start exploring the Zig modules.
Also, a great alternative is to read code from other large Zig codebases, such as:
All these assets are available on GitHub, and this is great, because we can use the GitHub search bar in our advantage, to find Zig code that fits our description. For example, you can always include lang:Zig in the GitHub search bar when you are searching for a particular pattern. This will limit the search to only Zig modules.
Also, a great alternative is to consult online resources and documentations. Here is a quick list of resources that I personally use from time to time to learn more about the language each day:
Another great strategy to learn Zig, or honestly, to learn any language you want, is to practice it by solving exercises. For example, there is a famous repository in the Zig community called Ziglings14 , which contains more than 100 small exercises that you can solve. It is a repository of tiny programs written in Zig that are currently broken, and your responsibility is to fix these programs, and make them work again.
-
A famous tech YouTuber known as The Primeagen also posted some videos (at YouTube) where he solves these exercises from Ziglings. The first video is named “Trying Zig Part 1”15.
-
Another great alternative, is to solve the Advent of Code exercises16. There are people that already took the time to learn and solve the exercises, and they posted their solutions on GitHub as well, so, in case you need some resource to compare while solving the exercises, you can look at these two repositories:
+
Another great strategy to learn Zig, or honestly, to learn any language you want, is to practice it by solving exercises. For example, there is a famous repository in the Zig community called Ziglings17 , which contains more than 100 small exercises that you can solve. It is a repository of tiny programs written in Zig that are currently broken, and your responsibility is to fix these programs, and make them work again.
+
A famous tech YouTuber known as The Primeagen also posted some videos (at YouTube) where he solves these exercises from Ziglings. The first video is named “Trying Zig Part 1”18.
+
Another great alternative, is to solve the Advent of Code exercises19. There are people that already took the time to learn and solve the exercises, and they posted their solutions on GitHub as well, so, in case you need some resource to compare while solving the exercises, you can look at these two repositories:
Zig have many different primitive data types available for you to use. You can see the full list of available data types at the official Language Reference page17.
+
Zig have many different primitive data types available for you to use. You can see the full list of available data types at the official Language Reference page20.
// This is a string object:const object: []constu8 = "A string object";
-
Zig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every sequence of bytes you’re working with, but is not really Zig’s job to fix the encoding of your strings (you can use iconv18 for that). Today, most of the text in our modern world, especially on the web, should be UTF-8 encoded. So if your string literal is not UTF-8 encoded, then, you will likely have problems in Zig.
+
Zig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every sequence of bytes you’re working with, but is not really Zig’s job to fix the encoding of your strings (you can use iconv21 for that). Today, most of the text in our modern world, especially on the web, should be UTF-8 encoded. So if your string literal is not UTF-8 encoded, then, you will likely have problems in Zig.
Let’s take for example the word “Hello”. In UTF-8, this sequence of characters (H, e, l, l, o) is represented by the sequence of decimal numbers 72, 101, 108, 108, 111. In hexadecimal, this sequence is 0x48, 0x65, 0x6C, 0x6C, 0x6F. So if I take this sequence of hexadecimal values, and ask Zig to print this sequence of bytes as a sequence of characters (i.e. a string), then, the text “Hello” will be printed into the terminal:
const std = @import("std");
@@ -831,7 +834,7 @@
Hello
-
If you want to see the actual bytes that represents a string in Zig, you can use a for loop to iterate through each byte in the string, and ask Zig to print each byte as an hexadecimal value to the terminal. You do that by using a print() statement with the X formatting specifier, like you would normally do with the printf() function19 in C.
+
If you want to see the actual bytes that represents a string in Zig, you can use a for loop to iterate through each byte in the string, and ask Zig to print each byte as an hexadecimal value to the terminal. You do that by using a print() statement with the X formatting specifier, like you would normally do with the printf() function22 in C.
A general trend in modern low-level programming languages is safety. As our modern world becomes more interconnected with technology and computers, the data produced by all of this technology becomes one of the most important (and also, one of the most dangerous) assets that we have.
This is probably the main reason why modern low-level programming languages have been giving great attention to safety, especially memory safety, because memory corruption is still the main target for hackers to exploit. The reality is that we don’t have an easy solution for this problem. For now, we only have techniques and strategies that mitigates these problems.
-
As Richard Feldman explains on his most recent GOTO conference talk20 , we haven’t figured it out yet a way to achieve true safety in technology. In other words, we haven’t found a way to build software that won’t be exploited with 100% certainty. We can greatly reduce the risks of our software being exploited, by ensuring memory safety for example. But this is not enough to achieve “true safety” territory.
-
Because even if you write your program in a “safe language”, hackers can still exploit failures in the operational system where your program is running (e.g. maybe the system where your code is running has a “backdoor exploit” that can still affect your code in unexpected ways), or also, they can exploit the features from the architecture of your computer. A recently found exploit that involves memory invalidation through a feature of “memory tags” present in ARM chips is an example of that (Kim et al. 2024).
-
The question is: what have Zig and other languages been doing to mitigate this problem? If we take Rust as an example, Rust is, for the most part21, a memory safe language by enforcing specific rules to the developer. In other words, the key feature of Rust, the borrow checker, forces you to follow a specific logic when you are writing your Rust code, and the Rust compiler will always complain everytime you try to go out of this pattern.
+
As Richard Feldman explains on his most recent GOTO conference talk23 , we haven’t figured it out yet a way to achieve true safety in technology. In other words, we haven’t found a way to build software that won’t be exploited with 100% certainty. We can greatly reduce the risks of our software being exploited, by ensuring memory safety for example. But this is not enough to achieve “true safety” territory.
+
Because even if you write your program in a “safe language”, hackers can still exploit failures in the operating system where your program is running (e.g. maybe the system where your code is running has a “backdoor exploit” that can still affect your code in unexpected ways), or also, they can exploit the features from the architecture of your computer. A recently found exploit that involves memory invalidation through a feature of “memory tags” present in ARM chips is an example of that (Kim et al. 2024).
+
The question is: what have Zig and other languages been doing to mitigate this problem? If we take Rust as an example, Rust is, for the most part24, a memory safe language by enforcing specific rules to the developer. In other words, the key feature of Rust, the borrow checker, forces you to follow a specific logic when you are writing your Rust code, and the Rust compiler will always complain everytime you try to go out of this pattern.
In contrast, the Zig language is not a memory safe language by default. There are some memory safety features that you get for free in Zig, especially in arrays and pointer objects. But there are other tools offered by the language, that are not used by default. In other words, the zig compiler does not obligates you to use such tools.
The tools listed below are related to memory safety. That is, they help you to achieve memory safety in your Zig code:
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.↩︎
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.↩︎
In this chapter, we are going to talk about the build system, and how an entire project is built in Zig. One key advantage of Zig is that it includes a build system embedded in the language itself. This is great, because then you do not have to depend on an external system, separated from the compiler, to build your code.
You can find a good description of Zig’s build system on the article entitled “Build System”1 from the official Zig’s website. We also have the excellent series of posts written by Felix2. Hence, this chapter represents an extra resource for you to consult and rely on.
-
Building code is one of the things that Zig is best at. One thing that is particularly difficult in C/C++ and even in Rust, is to cross-compile source code to multiple targets (e.g. multiple computer architectures and operational systems), and the zig compiler is known for being one of the best existing pieces of software for this particular task.
+
Building code is one of the things that Zig is best at. One thing that is particularly difficult in C/C++ and even in Rust, is to cross-compile source code to multiple targets (e.g. multiple computer architectures and operating systems), and the zig compiler is known for being one of the best existing pieces of software for this particular task.
9.1 How source code is built?
We already have talked about the challenges of building source code in low-level languages at Section 1.2.1. As we described at that section, programmers invented Build Systems to surpass these challenges on the process of building source code in low-level languages.
@@ -385,10 +385,10 @@
});
The name option specify the name that you want to give to the binary file defined by this target object. So, in this example, we are building an executable file named hello. Is common to set this name option to the name of your project.
-
Furthermore, the target option specify the target computer architecture (or the target operational system) of this binary file. For example, if you want this target object to run on a Windows machine that uses a x86_64 architecture, you can set this target option to x86_64-windows-gnu for example. This will make the zig compiler to compile the project to run on a x86_64 Windows machine. You can see the full list of architectures and OS’s that the zig compiler supports by running the zig targets command in the terminal.
+
Furthermore, the target option specify the target computer architecture (or the target operating system) of this binary file. For example, if you want this target object to run on a Windows machine that uses a x86_64 architecture, you can set this target option to x86_64-windows-gnu for example. This will make the zig compiler to compile the project to run on a x86_64 Windows machine. You can see the full list of architectures and OS’s that the zig compiler supports by running the zig targets command in the terminal.
Now, if you are building the project to run on the current machine that you are using to run this build script, you can set this target option to the host method of the Build object, like we did in the example above. This host method identifies the current machine where you are currently running the zig compiler.
At last, the root_source_file option specifies the root Zig module of your project. That is the Zig module that contains the entrypoint to your application (i.e. the main() function), or, the main API of your library. This also means that, all the Zig modules that compose your project are automatically discovered from the import statements you have inside this “root source file”. The zig compiler can detect when a Zig module depends on the other through the import statements, and, as a result, it can discover the entire map of Zig modules used in your project.
-
This is handy, and it is different from what happens in other build systems. In CMake for example, you have to explicitly list the paths to all source files that you want to include in your build process. This is probably a symptom of the “lack of conditional compilation” in the C and C++ compilers. Since they lack this feature, you have to explicitly choose which source files should be sent to the C/C++ compiler, because not every C/C++ code is portable or supported in every operational system, and, therefore, would cause a compilation error in the C/C++ compiler.
+
This is handy, and it is different from what happens in other build systems. In CMake for example, you have to explicitly list the paths to all source files that you want to include in your build process. This is probably a symptom of the “lack of conditional compilation” in the C and C++ compilers. Since they lack this feature, you have to explicitly choose which source files should be sent to the C/C++ compiler, because not every C/C++ code is portable or supported in every operating system, and, therefore, would cause a compilation error in the C/C++ compiler.
Now, one important detail about the build process is that, you have to explicitly install the target objects that you create in your build script, by using the installArtifact() method of the Build struct.
Everytime you invoke the build process of your project, by calling the build command of the zig compiler, a new directory named zig-out is created in the root directory of your project. This new directory contains the outputs of the build process, that is, the binary files built from your source code.
What the installArtifact() method do is to install (or copy) the built target objects that you defined to this zig-out directory. This means that, if you do not install the target objects you define in your build script, these target objects are essentially discarded at the end of the build process.
This means that we receive an input, or send some output, through the operating system. It is the OS that makes the bridge between the user and your program. Your program does not have a direct access to the user. It is the OS that intermediates every message exchanged between your program and the user.
The standard output and standard input channels of your OS are commonly known as the stdout and stdin channels of your OS, respectively. In some contexts, they are also called the standard output device and the standard input device. As the name suggests, the standard output is the channel through which output flows, while the standard input is the channel in which input flows.
Furthermore, OS’s also normally create a dedicated channel for exchanging error messages, which is known as the standard error channel, or, the stderr channel. This is the channel to which error and warning messages are usually sent to. These are the messages that are normally displayed in red-like or orange-like colors into your terminal.
-
Normally, every OS (e.g. Windows, MacOS, Linux, etc.) creates a dedicated and separate set of standard output, standard error and standard input channels for every single program (or process) that runs in your computer. This means that every program you write have a dedicated stdin, stderr and stdout that are separate from the stdin, stderr and stdout of other programs and processes that are currently running.
+
Normally, every OS (e.g. Windows, macOS, Linux, etc.) creates a dedicated and separate set of standard output, standard error and standard input channels for every single program (or process) that runs in your computer. This means that every program you write have a dedicated stdin, stderr and stdout that are separate from the stdin, stderr and stdout of other programs and processes that are currently running.
This is a behaviour from your OS. This does not come from the programming language that you are using. Because as I sad earlier, input and output in programming languages, especially in high-level ones, are just a simple abstraction over the stdin, stderr and stdout from your current OS. That is, your OS is the intermediary between every input/output operation made in your program, regardless of the programming language that you are using.
13.1.1 The writer and reader pattern
@@ -589,7 +589,7 @@
13.3.2 The concept of paths
A path is essentially a location. It points to a location in your filesystem. We use paths to describe the location of files and folders in our computer. One important aspect about paths is that they are always written inside strings, i.e. they are always provided as text values.
There are two types of paths that you can provide to any program in any OS: a relative path, or an absolute path. Absolute paths are paths that start at the root of your filesystem, and go all the way to the file name or the specific folder that you are referring to. This type of path is called absolute, because it points to an unique and absolute location on your computer. That is, there is no other existing location on your computer that corresponds to this path. It is an unique identifier.
-
In Windows, an absolute path is a path that starts with a hard disk identifier (e.g. C:/Users/pedro). On the other hand, absolute paths in Linux and MacOS, are paths that start with a forward slash character (e.g. /usr/local/bin). Notice that a path is composed by “segments”. Each segment is connected to each other by a slash character (\ or /). On Windows, the backward slash (\) is normally used to connect the path segments. While on Linux and MacOS, the forward slash (/) is the character used to connect path segments.
+
In Windows, an absolute path is a path that starts with a hard disk identifier (e.g. C:/Users/pedro). On the other hand, absolute paths in Linux and macOS, are paths that start with a forward slash character (e.g. /usr/local/bin). Notice that a path is composed by “segments”. Each segment is connected to each other by a slash character (\ or /). On Windows, the backward slash (\) is normally used to connect the path segments. While on Linux and macOS, the forward slash (/) is the character used to connect path segments.
A relative path is a path that start at the CWD. In other words, a relative path is “relative to the CWD”. The path used to access the hello.zig file at Figure 13.3 is an example of a relative path. This path is reproduced below. This path begins at the CWD, which in the context of Figure 13.3, is the zig-book folder, then, it goes to the ZigExamples folder, then, into zig-basics, then, to the hello.zig file.
This book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):
-
Calin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang)
+
Calin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur)
diff --git a/docs/search.json b/docs/search.json
index 7c847548..82133e70 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -84,7 +84,7 @@
"href": "index.html#acknowledgments",
"title": "Introduction to Zig",
"section": "Acknowledgments",
- "text": "Acknowledgments\nThis book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):\nCalin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang)",
+ "text": "Acknowledgments\nThis book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):\nCalin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur)",
"crumbs": [
"Welcome"
]
From afc69bd483d8fe72af8d2626655c4354226bdc52 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Thu, 30 Jan 2025 23:43:51 -0300
Subject: [PATCH 033/151] Recompile book with changes
---
.../01-zig-weird/execute-results/html.json | 8 +++++---
.../03-structs/execute-results/html.json | 4 ++--
docs/Chapters/01-zig-weird.html | 8 ++++----
docs/Chapters/03-structs.html | 16 ++++++++--------
docs/search.json | 12 ++++++------
5 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/_freeze/Chapters/01-zig-weird/execute-results/html.json b/_freeze/Chapters/01-zig-weird/execute-results/html.json
index 11a7f613..28800571 100644
--- a/_freeze/Chapters/01-zig-weird/execute-results/html.json
+++ b/_freeze/Chapters/01-zig-weird/execute-results/html.json
@@ -1,9 +1,11 @@
{
- "hash": "3e66c278de9f34fb70ff9abb53f3d4ce",
+ "hash": "507166385ff85ccf512b153b7c107291",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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\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\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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\n\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\n\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 successfully. 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\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\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 successfully.\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\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;\n1. 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 successfully compiled our source code.\n\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\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\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\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 objects 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\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\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 common 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 {#sec-select-array-elem}\n\nOne very common 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\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\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\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\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\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\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\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\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 accompanied 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\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\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\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\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\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\n### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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\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\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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\n\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\n\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\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\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\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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. As an example, the `string_object` object below is 43 bytes long:\n\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\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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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\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.write(\n \"Bytes that represents the string object: \"\n );\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligates you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
- "supporting": [],
+ "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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 compile successfully. 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 successfully.\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;\n1. 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 successfully 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 objects 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 common 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 {#sec-select-array-elem}\n\nOne very common 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 accompanied 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### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\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 the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. 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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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.write(\n \"Bytes that represents the string object: \"\n );\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();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
+ "supporting": [
+ "01-zig-weird_files"
+ ],
"filters": [
"rmarkdown/pagebreak.lua"
],
diff --git a/_freeze/Chapters/03-structs/execute-results/html.json b/_freeze/Chapters/03-structs/execute-results/html.json
index 04f37e1b..fb55828f 100644
--- a/_freeze/Chapters/03-structs/execute-results/html.json
+++ b/_freeze/Chapters/03-structs/execute-results/html.json
@@ -1,8 +1,8 @@
{
- "hash": "cedb7b341aa333220e9f4f2918e4ea74",
+ "hash": "75a34188e80fdc2dce5441514f1bc59a",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n# Control flow, structs, modules and types\n\nWe have discussed a lot of Zig's syntax in the last chapter,\nespecially in @sec-root-file and @sec-main-file.\nBut we still need to discuss some other very important\nelements of the language. Elements that you will use constantly on your day-to-day\nroutine.\n\nWe begin this chapter by discussing the different keywords and structures\nin Zig related to control flow (e.g. loops and if statements).\nThen, we talk about structs and how they can be used to do some\nbasic Object-Oriented (OOP) patterns in Zig. We also talk about\ntype inference and type casting.\nFinally, we end this chapter by discussing modules, and how they relate\nto structs.\n\n\n\n## Control flow {#sec-zig-control-flow}\n\nSometimes, you need to make decisions in your program. Maybe you need to decide\nwhether or not to execute a specific piece of code. Or maybe,\nyou need to apply the same operation over a sequence of values. These kinds of tasks,\ninvolve using structures that are capable of changing the \"control flow\" of our program.\n\nIn computer science, the term \"control flow\" usually refers to the order in which expressions (or commands)\nare evaluated in a given language or program. But this term is also used to refer\nto structures that are capable of changing this \"evaluation order\" of the commands\nexecuted by a given language/program.\n\nThese structures are better known\nby a set of terms, such as: loops, if/else statements, switch statements, among others. So,\nloops and if/else statements are examples of structures that can change the \"control\nflow\" of our program. The keywords `continue` and `break` are also examples of symbols\nthat can change the order of evaluation, since they can move our program to the next iteration\nof a loop, or make the loop stop completely.\n\n\n### If/else statements\n\nAn if/else statement performs a \"conditional flow operation\".\nA conditional flow control (or choice control) allows you to execute\nor ignore a certain block of commands based on a logical condition.\nMany programmers and computer science professionals also use\nthe term \"branching\" in this case.\nIn essence, an if/else statement allow us to use the result of a logical test\nto decide whether or not to execute a given block of commands.\n\nIn Zig, we write if/else statements by using the keywords `if` and `else`.\nWe start with the `if` keyword followed by a logical test inside a pair\nof parentheses, followed by a pair of curly braces which contains the lines\nof code to be executed in case the logical test returns the value `true`.\n\nAfter that, you can optionally add an `else` statement. To do that, just add the `else`\nkeyword followed by a pair of curly braces, with the lines of code\nto executed in case the logical test defined at `if` returns `false`.\n\nIn the example below, we are testing if the object `x` contains a number\nthat is greater than 10. Judging by the output printed to the console,\nwe know that this logical test returned `false`. Because the output\nin the console is compatible with the line of code present in the\n`else` branch of the if/else statement.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst x = 5;\nif (x > 10) {\n try stdout.print(\n \"x > 10!\\n\", .{}\n );\n} else {\n try stdout.print(\n \"x <= 10!\\n\", .{}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nx <= 10!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Switch statements {#sec-switch}\n\nSwitch statements are also available in Zig, and they have a very similar syntax to a switch statement in Rust.\nAs you would expect, to write a switch statement in Zig we use the `switch` keyword.\nWe provide the value that we want to \"switch over\" inside a\npair of parentheses. Then, we list the possible combinations (or \"branches\")\ninside a pair of curly braces.\n\nLet's take a look at the code example below. You can see that\nI'm creating an enum type called `Role`. We talk more about enums in @sec-enum.\nBut in summary, this `Role` type is listing different types of roles in a fictitious\ncompany, like `SE` for Software Engineer, `DE` for Data Engineer, `PM` for Product Manager,\netc.\n\nNotice that we are using the value from the `role` object in the\nswitch statement, to discover which exact area we need to store in the `area` variable object.\nAlso notice that we are using type inference inside the switch statement, with the dot character,\nas we are going to describe in @sec-type-inference.\nThis makes the `zig` compiler infer the correct data type of the values (`PM`, `SE`, etc.) for us.\n\nAlso notice that, we are grouping multiple values in the same branch of the switch statement.\nWe just separate each possible value with a comma. For example, if `role` contains either `DE` or `DA`,\nthe `area` variable would contain the value `\"Data & Analytics\"`, instead of `\"Platform\"` or `\"Sales\"`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst Role = enum {\n SE, DPE, DE, DA, PM, PO, KS\n};\n\npub fn main() !void {\n var area: []const u8 = undefined;\n const role = Role.SE;\n switch (role) {\n .PM, .SE, .DPE, .PO => {\n area = \"Platform\";\n },\n .DE, .DA => {\n area = \"Data & Analytics\";\n },\n .KS => {\n area = \"Sales\";\n },\n }\n try stdout.print(\"{s}\\n\", .{area});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nPlatform\n```\n\n\n:::\n:::\n\n\n\n\n\n#### Switch statements must exhaust all possibilities\n\nOne very important aspect about switch statements in Zig\nis that they must exhaust all existing possibilities.\nIn other words, all possible values that could be found inside the `order`\nobject must be explicitly handled in this switch statement.\n\nSince the `role` object have type `Role`, the only possible values to\nbe found inside this object are `PM`, `SE`, `DPE`, `PO`, `DE`, `DA` and `KS`.\nThere are no other possible values to be stored in this `role` object.\nThus, the switch statements must have a combination (branch) for each one of these values.\nThis is what \"exhaust all existing possibilities\" means. The switch statement covers\nevery possible case.\n\nTherefore, you cannot write a switch statement in Zig, and leave an edge case\nwith no explicit action to be taken.\nThis is a similar behaviour to switch statements in Rust, which also have to\nhandle all possible cases.\n\n\n\n#### The else branch\n\nTake a look at the `dump_hex_fallible()` function below as an example. This function\ncomes from the Zig Standard Library. More precisely, from the\n[`debug.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/debug.zig)[^debug-mod].\nThere are multiple lines in this function, but I omitted them to focus solely on the\nswitch statement found in this function. Notice that this switch statement has four\npossible cases (i.e. four explicit branches). Also, notice that we used an `else` branch\nin this case.\n\nAn `else` branch in a switch statement works as the \"default branch\".\nWhenever you have multiple cases in your switch statement where\nyou want to apply the exact same action, you can use an `else` branch to do that.\n\n[^debug-mod]: \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub fn dump_hex_fallible(bytes: []const u8) !void {\n // Many lines ...\n switch (byte) {\n '\\n' => try writer.writeAll(\"␊\"),\n '\\r' => try writer.writeAll(\"␍\"),\n '\\t' => try writer.writeAll(\"␉\"),\n else => try writer.writeByte('.'),\n }\n}\n```\n:::\n\n\n\n\nMany programmers would also use an `else` branch to handle a \"not supported\" case.\nThat is, a case that cannot be properly handled by your code, or, just a case that\nshould not be \"fixed\". Therefore, you can use an `else` branch to panic (or raise an error)\nin your program to stop the current execution.\n\nTake the code example below. We can see that, we are handling the cases\nfor the `level` object being either 1, 2, or 3. All other possible cases are not supported by default,\nand, as consequence, we raise a runtime error in such cases through the `@panic()` built-in function.\n\nAlso notice that, we are assigning the result of the switch statement to a new object called `category`.\nThis is another thing that you can do with switch statements in Zig. If a branch\noutputs a value as result, you can store the result value of the switch statement into\na new object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 1, 2 => \"beginner\",\n 3 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n:::\n\n\n\n\n```\nthread 13103 panic: Not supported level!\nt.zig:9:13: 0x1033c58 in main (switch2)\n @panic(\"Not supported level!\");\n ^\n```\n\n\n\n#### Using ranges in switch\n\nFurthermore, you can also use ranges of values in switch statements.\nThat is, you can create a branch in your switch statement that is used\nwhenever the input value is within the specified range. These \"range expressions\"\nare created with the operator `...`. It is important\nto emphasize that the ranges created by this operator are\ninclusive on both ends.\n\nFor example, I could easily change the previous code example to support all\nlevels between 0 and 100. Like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 0...25 => \"beginner\",\n 26...75 => \"intermediary\",\n 76...100 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nbeginner\n```\n\n\n:::\n:::\n\n\n\n\nThis is neat, and it works with character ranges too. That is, I could\nsimply write `'a'...'z'`, to match any character value that is a\nlowercase letter, and it would work fine.\n\n\n#### Labeled switch statements\n\nIn @sec-blocks we have talked about labeling blocks, and also, about using these labels\nto return a value from the block. Well, from version 0.14.0 and onwards of the `zig` compiler,\nyou can also apply labels over switch statements, which makes it possible to almost implement a\n\"C `goto`\" like pattern.\n\nFor example, if you give the label `xsw` to a switch statement, you can use this\nlabel in conjunction with the `continue` keyword to go back to the beginning of the switch\nstatement. In the example below, the execution goes back to the beginning of the\nswitch statement two times, before ending at the `3` branch.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nxsw: switch (@as(u8, 1)) {\n 1 => {\n try stdout.print(\"First branch\\n\", .{});\n continue :xsw 2;\n },\n 2 => continue :xsw 3,\n 3 => return,\n 4 => {},\n else => {\n try stdout.print(\n \"Unmatched case, value: {d}\\n\", .{@as(u8, 1)}\n );\n },\n}\n```\n:::\n\n\n\n\n\n### The `defer` keyword {#sec-defer}\n\nWith the `defer` keyword you can register an expression to be executed when you exit the current scope.\nTherefore, this keyword has a similar functionality as the `on.exit()` function from R.\nTake the `foo()` function below as an example. When we execute this `foo()` function, the expression\nthat prints the message \"Exiting function ...\" is getting executed only when the function exits\nits scope.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn foo() !void {\n defer std.debug.print(\n \"Exiting function ...\\n\", .{}\n );\n try stdout.print(\"Adding some numbers ...\\n\", .{});\n const x = 2 + 2; _ = x;\n try stdout.print(\"Multiplying ...\\n\", .{});\n const y = 2 * 8; _ = y;\n}\n\npub fn main() !void {\n try foo();\n}\n```\n:::\n\n\n\n\n```\nAdding some numbers ...\nMultiplying ...\nExiting function ...\n```\n\nTherefore, we can use `defer` to declare an expression that is going to be executed\nwhen your code exits the current scope. Some programmers like to interpret the phrase \"exit of the current scope\"\nas \"the end of the current scope\". But this interpretation might not be entirely correct, depending\non what you consider as \"the end of the current scope\".\n\nI mean, what do you consider as **the end** of the current scope? Is it the closing curly bracket (`}`) of the scope?\nIs it when the last expression in the function get's executed? Is it when the function returns to the previous scope?\nEtc. For example, it would not be correct to interpret the \"exit of the current scope\" as the closing\ncurly bracket of the scope. Because the function might exit from an earlier position than this\nclosing curly bracket (e.g. an error value was generated at a previous line inside the function;\nthe function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.\n\nNow, if you remember of what we have discussed in @sec-blocks, there are multiple structures in the language\nthat create their own separate scopes. For/while loops, if/else statements,\nfunctions, normal blocks, etc. This also affects the interpretation of `defer`.\nFor example, if you use `defer` inside a for loop, then, the given expression\nwill be executed everytime this specific for loop exits its own scope.\n\nBefore we continue, is worth emphasizing that the `defer` keyword is an \"unconditional defer\".\nWhich means that the given expression will be executed no matter how the code exits\nthe current scope. For example, your code might exit the current scope because of an error value\nbeing generated, or, because of a return statement, or, a break statement, etc.\n\n\n\n### The `errdefer` keyword {#sec-errdefer1}\n\nOn the previous section, we have discussed the `defer` keyword, which you can use to\nregister an expression to be executed at the exit of the current scope.\nBut this keyword has a brother, which is the `errdefer` keyword. While `defer`\nis an \"unconditional defer\", the `errdefer` keyword is a \"conditional defer\".\nWhich means that the given expression is executed only when you exit the current\nscope on a very specific circumstance.\n\nIn more details, the expression given to `errdefer` is executed only when an error occurs in the current scope.\nTherefore, if the function (or for/while loop, if/else statement, etc.) exits the current scope\nin a normal situation, without errors, the expression given to `errdefer` is not executed.\n\nThis makes the `errdefer` keyword one of the many tools available in Zig for error handling.\nIn this section, we are more concerned with the control flow aspects around `errdefer`.\nBut we are going to discuss `errdefer` later as a error handling tool in @sec-errdefer2.\n\nThe code example below demonstrates three things:\n\n- that `defer` is an \"unconditional defer\", because the given expression get's executed regardless of how the function `foo()` exits its own scope.\n- that `errdefer` is executed because the function `foo()` returned an error value.\n- that `defer` and `errdefer` expressions are executed in a LIFO (*last in, first out*) order.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n defer i = 2;\n try foo();\n}\n```\n:::\n\n\n\n\n```\nValue of i: 2\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\nWhen I say that \"defer expressions\" are executed in a LIFO order, what I want to say is that\nthe last `defer` or `errdefer` expressions in the code are the first ones to be executed.\nYou could also interpret this as: \"defer expressions\" are executed from bottom to top, or,\nfrom last to first.\n\nTherefore, if I change the order of the `defer` and `errdefer` expressions, you will notice that\nthe value of `i` that get's printed to the console changes to 1. This doesn't mean that the\n`defer` expression was not executed in this case. This actually means that the `defer` expression\nwas executed only after the `errdefer` expression. The code example below demonstrates this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n defer i = 2;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n try foo();\n}\n```\n:::\n\n\n\n\n```\nValue of i: 1\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\n\n\n### For loops\n\nA loop allows you to execute the same lines of code multiple times,\nthus, creating a \"repetition space\" in the execution flow of your program.\nLoops are particularly useful when we want to replicate the same function\n(or the same set of commands) over different inputs.\n\nThere are different types of loops available in Zig. But the most\nessential of them all is probably the *for loop*. A for loop is\nused to apply the same piece of code over the elements of a slice, or, an array.\n\nFor loops in Zig use a syntax that may be unfamiliar to programmers coming from\nother languages. You start with the `for` keyword, then, you\nlist the items that you want to iterate\nover inside a pair of parentheses. Then, inside of a pair of pipes (`|`)\nyou should declare an identifier that will serve as your iterator, or,\nthe \"repetition index of the loop\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (items) |value| {\n // code to execute\n}\n```\n:::\n\n\n\n\nTherefore, instead of using a `(value in items)` syntax,\nin Zig, for loops use the syntax `(items) |value|`. In the example\nbelow, you can see that we are looping through the items\nof the array stored at the object `name`, and printing to the\nconsole the decimal representation of each character in this array.\n\nIf we wanted, we could also iterate through a slice (or a portion) of\nthe array, instead of iterating through the entire array stored in the `name` object.\nJust use a range selector to select the section you want. For example,\nI could provide the expression `name[0..3]` to the for loop, to iterate\njust through the first 3 elements in the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name = [_]u8{'P','e','d','r','o'};\nfor (name) |char| {\n try stdout.print(\"{d} | \", .{char});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n80 | 101 | 100 | 114 | 111 | \n```\n\n\n:::\n:::\n\n\n\n\nIn the above example we are using the value itself of each\nelement in the array as our iterator. But there are many situations where\nwe need to use an index instead of the actual values of the items.\n\nYou can do that by providing a second set of items to iterate over.\nMore precisely, you provide the range selector `0..` to the for loop. So,\nyes, you can use two different iterators at the same time in a for\nloop in Zig.\n\nBut remember from @sec-assignments that, every object\nyou create in Zig must be used in some way. So if you declare two iterators\nin your for loop, you must use both iterators inside the for loop body.\nBut if you want to use just the index iterator, and not use the \"value iterator\",\nthen, you can discard the value iterator by maching the\nvalue items to the underscore character, like in the example below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name = \"Pedro\";\nfor (name, 0..) |_, i| {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n:::\n\n\n\n\n```\n0 | 1 | 2 | 3 | 4 |\n```\n\n\n### While loops\n\nA while loop is created from the `while` keyword. A `for` loop\niterates through the items of an array, but a `while` loop\nwill loop continuously, and infinitely, until a logical test\n(specified by you) becomes false.\n\nYou start with the `while` keyword, then, you define a logical\nexpression inside a pair of parentheses, and the body of the\nloop is provided inside a pair of curly braces, like in the example below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) {\n try stdout.print(\"{d} | \", .{i});\n i += 1;\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\nYou can also specify the increment expression to be used at the beginning of a while loop.\nTo do that, we write the increment expression inside a pair of parentheses after a colon character (`:`).\nThe code example below demonstrates this other pattern.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) : (i += 1) {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\n### Using `break` and `continue`\n\nIn Zig, you can explicitly stop the execution of a loop, or, jump to the next iteration of the loop, by using\nthe keywords `break` and `continue`, respectively. The `while` loop presented in the next code example is,\nat first sight, an infinite loop. Because the logical value inside the parenthese will always be equal to `true`.\nBut what makes this `while` loop stop when the `i` object reaches the count\n10? It is the `break` keyword!\n\nInside the while loop, we have an if statement that is constantly checking if the `i` variable\nis equal to 10. Since we are incrementing the value of `i` at each iteration of the\nwhile loop, this `i` object will eventually be equal to 10, and when it is, the if statement\nwill execute the `break` expression, and, as a result, the execution of the while loop is stopped.\n\nNotice the use of the `expect()` function from the Zig Standard Library after the while loop.\nThis `expect()` function is an \"assert\" type of function.\nThis function checks if the logical test provided is equal to true. If so, the function do nothing.\nOtherwise (i.e. the logical test is equal to false), the function raises an assertion error.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: usize = 0;\nwhile (true) {\n if (i == 10) {\n break;\n }\n i += 1;\n}\ntry std.testing.expect(i == 10);\ntry stdout.print(\"Everything worked!\", .{});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nEverything worked!\n```\n\n\n:::\n:::\n\n\n\n\nSince this code example was executed successfully by the `zig` compiler,\nwithout raising any errors, we known that, after the execution of the while loop,\nthe `i` object is equal to 10. Because if it wasn't equal to 10, an error would have\nbeen raised by `expect()`.\n\nNow, in the next example, we have a use case for\nthe `continue` keyword. The if statement is constantly\nchecking if the current index is a multiple of 2. If\nit is, we jump to the next iteration of the loop.\nOtherwise, the loop just prints the current index to the console.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [_]u8{1,2,3,4,5,6};\nfor (ns) |i| {\n if ((i % 2) == 0) {\n continue;\n }\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 3 | 5 | \n```\n\n\n:::\n:::\n\n\n\n\n\n\n## Function parameters are immutable {#sec-fun-pars}\n\nWe have already discussed a lot of the syntax behind function declarations in @sec-root-file and @sec-main-file.\nBut I want to emphasize a curious fact about function parameters (a.k.a. function arguments) in Zig.\nIn summary, function parameters are immutable in Zig.\n\nTake the code example below, where we declare a simple function that just tries to add\nsome amount to the input integer, and returns the result back. If you look closely\nat the body of this `add2()` function, you will notice that we try\nto save the result back into the `x` function argument.\n\nIn other words, this function not only uses the value that it received through the function argument\n`x`, but it also tries to change the value of this function argument, by assigning the addition result\ninto `x`. However, function arguments in Zig are immutable. You cannot change their values, or, you\ncannot assign values to them inside the body's function.\n\nThis is the reason why, the code example below does not compile successfully. If you try to compile\nthis code example, you will get a compile error message about \"trying to change the value of a\nimmutable (i.e. constant) object\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: u32) u32 {\n x = x + 2;\n return x;\n}\n\npub fn main() !void {\n const y = add2(4);\n std.debug.print(\"{d}\\n\", .{y});\n}\n```\n:::\n\n\n\n\n```\nt.zig:3:5: error: cannot assign to constant\n x = x + 2;\n ^\n```\n\n\n### A free optimization\n\nIf a function argument receives as input an object whose data type is\nany of the primitive types that we have listed in @sec-primitive-data-types,\nthis object is always passed by value to the function. In other words, this object\nis copied into the function stack frame.\n\nHowever, if the input object have a more complex data type, for example, it might\nbe a struct instance, or an array, or an union value, etc., in cases like that, the `zig` compiler\nwill take the liberty of deciding for you which strategy is best. Thus, the `zig` compiler will\npass your object to the function either by value, or by reference. The compiler will always\nchoose the strategy that is faster for you.\nThis optimization that you get for free is possible only because function arguments are\nimmutable in Zig.\n\n\n### How to overcome this barrier\n\nThere are some situations where you might need to change the value of your function argument\ndirectly inside the function's body. This happens more often when we are passing\nC structs as inputs to Zig functions.\n\nIn a situation like this, you can overcome this barrier by using a pointer. In other words,\ninstead of passing a value as input to the argument, you can pass a \"pointer to value\" instead.\nYou can change the value that the pointer points to, by dereferencing it.\n\nTherefore, if we take our previous `add2()` example, we can change the value of the\nfunction argument `x` inside the function's body by marking the `x` argument as a\n\"pointer to a `u32` value\" (i.e. `*u32` data type), instead of a `u32` value.\nBy making it a pointer, we can finally alter the value of this function argument directly inside\nthe body of the `add2()` function. You can see that the code example below compiles successfully.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: *u32) void {\n const d: u32 = 2;\n x.* = x.* + d;\n}\n\npub fn main() !void {\n var x: u32 = 4;\n add2(&x);\n std.debug.print(\"Result: {d}\\n\", .{x});\n}\n```\n:::\n\n\n\n\n```\nResult: 6\n```\n\n\nEven in this code example above, the `x` argument is still immutable. Which means that the pointer itself is immutable.\nTherefore, you cannot change the memory address that it points to. However, you can dereference the pointer\nto access the value that it points to, and also, to change this value, if you need to.\n\n\n\n\n\n## Structs and OOP {#sec-structs-and-oop}\n\nZig is a language more closely related to C (which is a procedural language),\nthan it is to C++ or Java (which are object-oriented languages). Because of that, you do not\nhave advanced OOP (Object-Oriented Programming) patterns available in Zig, such as classes, interfaces or\nclass inheritance. Nonetheless, OOP in Zig is still possible by using struct definitions.\n\nWith struct definitions, you can create (or define) a new data type in Zig. These struct definitions work the same way as they work in C.\nYou give a name to this new struct (or, to this new data type you are creating), then, you list the data members of this new struct. You can\nalso register functions inside this struct, and they become the methods of this particular struct (or data type), so that, every object\nthat you create with this new type, will always have these methods available and associated with them.\n\nIn C++, when we create a new class, we normally have a constructor method (or, a constructor function) which\nis used to construct (or, to instantiate) every object of this particular class, and we also have\na destructor method (or a destructor function), which is the function responsible for destroying\nevery object of this class.\n\nIn Zig, we normally declare the constructor and the destructor methods\nof our structs, by declaring an `init()` and a `deinit()` methods inside the struct.\nThis is just a naming convention that you will find across the entire Zig Standard Library.\nSo, in Zig, the `init()` method of a struct is normally the constructor method of the class represented by this struct.\nWhile the `deinit()` method is the method used for destroying an existing instance of that struct.\n\nThe `init()` and `deinit()` methods are both used extensively in Zig code, and you will see both of\nthem being used when we talk about allocators in @sec-allocators.\nBut, as another example, let's build a simple `User` struct to represent a user of some sort of system.\n\nIf you look at the `User` struct below, you can see the `struct` keyword.\nNotice the data members of this struct: `id`, `name` and `email`. Every data member has its\ntype explicitly annotated, with the colon character (`:`) syntax that we described earlier in @sec-root-file.\nBut also notice that every line in the struct body that describes a data member, ends with a comma character (`,`).\nSo every time you declare a data member in your Zig code, always end the line with a comma character, instead\nof ending it with the traditional semicolon character (`;`).\n\nNext, we have registered an `init()` function as a method\nof this `User` struct. This `init()` method is the constructor method that we will use to instantiate\nevery new `User` object. That is why this `init()` function returns a new `User` object as result.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst User = struct {\n id: u64,\n name: []const u8,\n email: []const u8,\n\n pub fn init(id: u64,\n name: []const u8,\n email: []const u8) User {\n\n return User {\n .id = id,\n .name = name,\n .email = email\n };\n }\n\n pub fn print_name(self: User) !void {\n try stdout.print(\"{s}\\n\", .{self.name});\n }\n};\n\npub fn main() !void {\n const u = User.init(1, \"pedro\", \"email@gmail.com\");\n try u.print_name();\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\npedro\n```\n\n\n:::\n:::\n\n\n\n\nThe `pub` keyword plays an important role in struct declarations, and OOP in Zig.\nEvery method that you declare in your struct that is marked with the keyword `pub`,\nbecomes a public method of this particular struct.\n\nSo every method that you create inside your struct, is, at first, a private method\nof that struct. Meaning that, this method can only be called from within this\nstruct. But, if you mark this method as public, with the keyword `pub`, then,\nyou can call the method directly from an instance of the `User` struct.\n\nIn other words, the functions marked by the keyword `pub`\nare members of the public API of that struct.\nFor example, if I did not mark the `print_name()` method as public,\nthen, I could not execute the line `u.print_name()`. Because I would\nnot be authorized to call this method directly in my code.\n\n\n\n### Anonymous struct literals {#sec-anonymous-struct-literals}\n\nYou can declare a struct object as a literal value. When we do that, we normally specify the\ndata type of this struct literal by writing its data type just before the opening curly brace.\nFor example, I could write a struct literal value of the type `User` that we have defined\nin the previous section like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst eu = User {\n .id = 1,\n .name = \"Pedro\",\n .email = \"someemail@gmail.com\"\n};\n_ = eu;\n```\n:::\n\n\n\n\nHowever, in Zig, we can also write an anonymous struct literal. That is, you can write a\nstruct literal, but not specify explicitly the type of this particular struct.\nAn anonymous struct is written by using the syntax `.{}`. So, we essentially\nreplaced the explicit type of the struct literal with a dot character (`.`).\n\nAs we described in @sec-type-inference, when you put a dot before a struct literal,\nthe type of this struct literal is automatically inferred by the `zig` compiler.\nIn essence, the `zig` compiler will look for some hint of what is the type of that struct.\nThis hint can be the type annotation of a function argument,\nor the return type annotation of the function that you are using, or the type annotation\nof an existing object.\nIf the compiler does find such type annotation, it will use this\ntype in your literal struct.\n\nAnonymous structs are very commonly used as inputs to function arguments in Zig.\nOne example that you have seen already constantly, is the `print()`\nfunction from the `stdout` object.\nThis function takes two arguments.\nThe first argument, is a template string, which should\ncontain string format specifiers in it, which tells how the values provided\nin the second argument should be printed into the message.\n\nWhile the second argument is a struct literal that lists the values\nto be printed into the template message specified in the first argument.\nYou normally want to use an anonymous struct literal here, so that the\n`zig` compiler do the job of specifying the type of this particular\nanonymous struct for you.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHello, world!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Struct declarations must be constant\n\nTypes in Zig must be `const` or `comptime` (we are going to talk more about comptime in @sec-comptime).\nWhat this means is that you cannot create a new data type, and mark it as variable with the `var` keyword.\nSo struct declarations are always constant. You cannot declare a new struct type using the `var` keyword.\nIt must be `const`.\n\nIn the `Vec3` example below, this declaration is allowed because I'm using the `const` keyword\nto declare this new data type.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n};\n```\n:::\n\n\n\n\n\n### The `self` method argument {#sec-self-arg}\n\nIn every language that have OOP, when we declare a method of some class or struct, we\nusually declare this method as a function that has a `self` argument.\nThis `self` argument is the reference to the object itself from which the method\nis being called from.\n\nIt is not mandatory to use this `self` argument. But why would you not use this `self` argument?\nThere is no reason to not use it. Because the only way to get access to the data stored in the\ndata members of your struct is to access them through this `self` argument.\nIf you don't need to use the data in the data members of your struct inside your method, you very likely don't need\na method. You can just declare this logic as a simple function, outside of your\nstruct declaration.\n\n\nTake the `Vec3` struct below. Inside this `Vec3` struct we declared a method named `distance()`.\nThis method calculates the distance between two `Vec3` objects, by following the distance\nformula in euclidean space. Notice that this `distance()` method takes two `Vec3` objects\nas input, `self` and `other`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n};\n```\n:::\n\n\n\n\n\nThe `self` argument corresponds to the `Vec3` object from which this `distance()` method\nis being called from. While the `other` is a separate `Vec3` object that is given as input\nto this method. In the example below, the `self` argument corresponds to the object\n`v1`, because the `distance()` method is being called from the `v1` object,\nwhile the `other` argument corresponds to the object `v2`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst v1 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nconst v2 = Vec3 {\n .x = 5.1, .y = 5.6, .z = 1.6\n};\n\nstd.debug.print(\n \"Distance: {d}\\n\",\n .{v1.distance(v2)}\n);\n```\n:::\n\n\n\n\n```\nDistance: 3.3970575502926055\n```\n\n\n\n### About the struct state\n\nSometimes you don't need to care about the state of your struct object. Sometimes, you just need\nto instantiate and use the objects, without altering their state. You can notice that when you have methods\ninside your struct declaration that might use the values that are present in the data members, but they\ndo not alter the values in these data members of the struct in anyway.\n\nThe `Vec3` struct that was presented in @sec-self-arg is an example of that.\nThis struct have a single method named `distance()`, and this method does use the values\npresent in all three data members of the struct (`x`, `y` and `z`). But at the same time,\nthis method does not change the values of these data members at any point.\n\nAs a result of that, when we create `Vec3` objects we usually create them as\nconstant objects, like the `v1` and `v2` objects presented in @sec-self-arg.\nWe can create them as variable objects with the `var` keyword,\nif we want to. But because the methods of this `Vec3` struct do not change\nthe state of the objects in any point, it's unnecessary to mark them\nas variable objects.\n\nBut why? Why am I talking about this here? It's because the `self` argument\nin the methods is affected depending on whether the\nmethods present in a struct change or don't change the state of the object itself.\nMore specifically, when you have a method in a struct that changes the state\nof the object (i.e. change the value of a data member), the `self` argument\nin this method must be annotated in a different manner.\n\nAs I described in @sec-self-arg, the `self` argument in methods of\na struct is the argument that receives as input the object from which the method\nwas called from. We usually annotate this argument in the methods by writing `self`,\nfollowed by the colon character (`:`), and the data type of the struct to which\nthe method belongs to (e.g. `User`, `Vec3`, etc.).\n\nIf we take the `Vec3` struct that we defined in the previous section as an example,\nwe can see in the `distance()` method that this `self` argument is annotated as\n`self: Vec3`. Because the state of the `Vec3` object is never altered by this\nmethod.\n\nBut what if we do have a method that alters the state of the object, by altering the\nvalues of its data members, how should we annotate `self` in this instance? The answer is:\n\"we should annotate `self` as a pointer of `x`, instead of just `x`\".\nIn other words, you should annotate `self` as `self: *x`, instead of annotating it\nas `self: x`.\n\nIf we create a new method inside the `Vec3` object that, for example, expands the\nvector by multiplying its coordinates by a factor of two, then, we need to follow\nthis rule specified in the previous paragraph. The code example below demonstrates\nthis idea:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n\n pub fn twice(self: *Vec3) void {\n self.x = self.x * 2.0;\n self.y = self.y * 2.0;\n self.z = self.z * 2.0;\n }\n};\n```\n:::\n\n\n\n\nNotice in the code example above that we have added a new method\nto our `Vec3` struct named `twice()`. This method doubles the\ncoordinate values of our vector object. In the\ncase of the `twice()` method, we annotated the `self` argument as `*Vec3`,\nindicating that this argument receives a pointer (or a reference, if you prefer to call it this way)\nto a `Vec3` object as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar v3 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nv3.twice();\nstd.debug.print(\"Doubled: {d}\\n\", .{v3.x});\n```\n:::\n\n\n\n\n```\nDoubled: 8.4\n```\n\n\n\nNow, if you change the `self` argument in this `twice()` method to `self: Vec3`, like in the\n`distance()` method, you will get the compiler error exposed below as result. Notice that this\nerror message is showing a line from the `twice()` method body,\nindicating that you cannot alter the value of the `x` data member.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// If we change the function signature of double to:\n pub fn twice(self: Vec3) void {\n```\n:::\n\n\n\n\n```\nt.zig:16:13: error: cannot assign to constant\n self.x = self.x * 2.0;\n ~~~~^~\n```\n\nThis error message indicates that the `x` data member belongs to a constant object,\nand, because of that, it cannot be changed. Ultimately, this error message\nis telling us that the `self` argument is constant.\n\nIf you take some time, and think hard about this error message, you will understand it.\nYou already have the tools to understand why we are getting this error message.\nWe have talked about it already in @sec-fun-pars.\nSo remember, every function argument is immutable in Zig, and `self`\nis no exception to this rule.\n\nIn this example, we marked the `v3` object as a variable object.\nBut this does not matter. Because it is not about the input object, it is about\nthe function argument.\n\nThe problem begins when we try to alter the value of `self` directly, which is a function argument,\nand, every function argument is immutable by default. You may ask yourself how can we overcome\nthis barrier, and once again, the solution was also discussed in @sec-fun-pars.\nWe overcome this barrier, by explicitly marking the `self` argument as a pointer.\n\n\n::: {.callout-note}\nIf a method of your `x` struct alters the state of the object, by\nchanging the value of any data member, then, remember to use `self: *x`,\ninstead of `self: x` in the function signature of this method.\n:::\n\n\nYou could also interpret the content discussed in this section as:\n\"if you need to alter the state of your `x` struct object in one of its methods,\nyou must explicitly pass the `x` struct object by reference to the `self` argument of this method\".\n\n\n\n## Type inference {#sec-type-inference}\n\nZig is a strongly typed language. But, there are some situations\nwhere you don't have to explicitly write the type of every single object in your source code,\nas you would expect from a traditional strongly typed language, such as C and C++.\n\nIn some situations, the `zig` compiler can use type inference to solve the data types for you, easing some of\nthe burden that you carry as a developer.\nThe most common way this happens is through function arguments that receive struct objects\nas input.\n\nIn general, type inference in Zig is done by using the dot character (`.`).\nEverytime you see a dot character written before a struct literal, or before an enum value, or something like that,\nyou know that this dot character is playing a special party in this place. More specifically, it is\ntelling the `zig` compiler something along the lines of: \"Hey! Can you infer the type of this\nvalue for me? Please!\". In other words, this dot character is playing a similar role as the `auto` keyword in C++.\n\nI gave you some examples of this in @sec-anonymous-struct-literals, where we used anonymous struct literals.\nAnonymous struct literals are, struct literals that use type inference to\ninfer the exact type of this particular struct literal.\nThis type inference is done by looking for some minimal hint of the correct data type to be used.\nYou could say that the `zig` compiler looks for any neighbouring type annotation that might tell it\nwhat the correct type would be.\n\nAnother common place where we use type inference in Zig is at switch statements (which we talked about in @sec-switch).\nI also gave some other examples of type inference in @sec-switch, where we were inferring the data types of enum values listed inside\nof switch statements (e.g. `.DE`).\nBut as another example, take a look at this `fence()` function reproduced below,\nwhich comes from the [`atomic.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/atomic.zig)[^fence-fn]\nof the Zig Standard Library.\n\n[^fence-fn]: .\n\nThere are a lot of things in this function that we haven't talked about yet, such as:\nwhat `comptime` means? `inline`? `extern`?\nLet's just ignore all of these things, and focus solely on the switch statement\nthat is inside this function.\n\nWe can see that this switch statement uses the `order` object as input. This `order`\nobject is one of the inputs of this `fence()` function, and we can see in the type annotation,\nthat this object is of type `AtomicOrder`. We can also see a bunch of values inside the\nswitch statements that begin with a dot character, such as `.release` and `.acquire`.\n\nBecause these weird values contain a dot character before them, we are asking the `zig`\ncompiler to infer the types of these values inside the switch statement. Then, the `zig`\ncompiler is looking into the current context where these values are being used, and it is\ntrying to infer the types of these values.\n\nSince they are being used inside a switch statement, the `zig` compiler looks into the type\nof the input object given to the switch statement, which is the `order` object in this case.\nBecause this object have type `AtomicOrder`, the `zig` compiler infers that these values\nare data members from this type `AtomicOrder`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub inline fn fence(self: *Self, comptime order: AtomicOrder) void {\n // many lines of code ...\n if (builtin.sanitize_thread) {\n const tsan = struct {\n extern \"c\" fn __tsan_acquire(addr: *anyopaque) void;\n extern \"c\" fn __tsan_release(addr: *anyopaque) void;\n };\n\n const addr: *anyopaque = self;\n return switch (order) {\n .unordered, .monotonic => @compileError(\n @tagName(order)\n ++ \" only applies to atomic loads and stores\"\n ),\n .acquire => tsan.__tsan_acquire(addr),\n .release => tsan.__tsan_release(addr),\n .acq_rel, .seq_cst => {\n tsan.__tsan_acquire(addr);\n tsan.__tsan_release(addr);\n },\n };\n }\n\n return @fence(order);\n}\n```\n:::\n\n\n\n\nThis is how basic type inference is done in Zig. If we didn't use the dot character before\nthe values inside this switch statement, then, we would be forced to explicitly write\nthe data types of these values. For example, instead of writing `.release` we would have to\nwrite `AtomicOrder.release`. We would have to do this for every single value\nin this switch statement, and this is a lot of work. That is why type inference\nis commonly used on switch statements in Zig.\n\n\n\n## Type casting {#sec-type-cast}\n\nIn this section, I want to discuss type casting (or, type conversion) with you.\nWe use type casting when we have an object of type \"x\", and we want to convert\nit into an object of type \"y\", i.e. we want to change the data type of the object.\n\nMost languages have a formal way to perform type casting. In Rust for example, we normally\nuse the keyword `as`, and in C, we normally use the type casting syntax, e.g. `(int) x`.\nIn Zig, we use the `@as()` built-in function to cast an object of type \"x\", into\nan object of type \"y\".\n\nThis `@as()` function is the preferred way to perform type conversion (or type casting)\nin Zig. Because it is explicit, and, it also performs the casting only if it\nis unambiguous and safe. To use this function, you just provide the target data type\nin the first argument, and, the object that you want cast as the second argument.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 500;\n const y = @as(u32, x);\n try expect(@TypeOf(y) == u32);\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1/1 file10b91234368fa.test_0...OKAll 1 tests passe\n ed.\n```\n\n\n:::\n:::\n\n\n\n\nThis is the general way to perform type casting in Zig. But remember, `@as()` works only when casting\nis unambiguous and safe, and there are situations where these assumptions do not hold. For example,\nwhen casting an integer value into a float value, or vice-versa, it is not clear to the compiler\nhow to perform this conversion safely.\n\nTherefore, we need to use specialized \"casting functions\" in such situations.\nFor example, if you want to cast an integer value into a float value, then, you\nshould use the `@floatFromInt()` function. In the inverse scenario, you should use\nthe `@intFromFloat()` function.\n\nIn these functions, you just provide the object that you want to\ncast as input. Then, the target data type of the \"type casting operation\" is determined by\nthe type annotation of the object where you are saving the results.\nIn the example below, we are casting the object `x` into a value of type `f32`,\nbecause the object `y`, which is where we are saving the results, is annotated\nas an object of type `f32`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 565;\n const y: f32 = @floatFromInt(x);\n try expect(@TypeOf(y) == f32);\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1/1 file10b9130ae9221.test_0...OKAll 1 tests passe\n ed.\n```\n\n\n:::\n:::\n\n\n\n\nAnother built-in function that is very useful when performing type casting operations is `@ptrCast()`.\nIn essence, we use the `@as()` built-in function when we want to explicit convert (or cast) a Zig value/object\nfrom a type \"x\" to a type \"y\", etc. However, pointers (we are going to discuss pointers\nin more depth in @sec-pointer) are a special type of object in Zig,\ni.e. they are treated differently from \"normal objects\".\n\nEverytime a pointer is involved in some \"type casting operation\" in Zig, the `@ptrCast()` function is used.\nThis function works similarly to `@floatFromInt()`.\nYou just provide the pointer object that you want to cast as input to this function, and the\ntarget data type is, once again, determined by the type annotation of the object where the results are being\nstored.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const bytes align(@alignOf(u32)) = [_]u8{\n 0x12, 0x12, 0x12, 0x12\n };\n const u32_ptr: *const u32 = @ptrCast(&bytes);\n try expect(@TypeOf(u32_ptr) == *const u32);\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1/1 file10b914c434861.test_0...OKAll 1 tests passe\n ed.\n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n\n## Modules\n\nWe already talked about what modules are, and also, how to import other modules into\nyour current module via *import statements*. Every Zig module (i.e. a `.zig` file) that you write in your project\nis internally stored as a struct object. Take the line exposed below as an example. In this line we are importing the\nZig Standard Library into our current module.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n```\n:::\n\n\n\n\nWhen we want to access the functions and objects from the standard library, we\nare basically accessing the data members of the struct stored in the `std`\nobject. That is why we use the same syntax that we use in normal structs, with the dot operator (`.`)\nto access the data members and methods of the struct.\n\nWhen this \"import statement\" get's executed, the result of this expression is a struct\nobject that contains the Zig Standard Library modules, global variables, functions, etc.\nAnd this struct object get's saved (or stored) inside the constant object named `std`.\n\n\nTake the [`thread_pool.zig` module from the project `zap`](https://github.com/kprotty/zap/blob/blog/src/thread_pool.zig)[^thread]\nas an example. This module is written as if it was\na big struct. That is why we have a top-level and public `init()` method\nwritten in this module. The idea is that all top-level functions written in this\nmodule are methods from the struct, and all top-level objects and struct declarations\nare data members of this struct. The module is the struct itself.\n\n[^thread]: \n\n\nSo you would import and use this module by doing something like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst ThreadPool = @import(\"thread_pool.zig\");\nconst num_cpus = std.Thread.getCpuCount()\n catch @panic(\"failed to get cpu core count\");\nconst num_threads = std.math.cast(u16, num_cpus)\n catch std.math.maxInt(u16);\nconst pool = ThreadPool.init(\n .{ .max_threads = num_threads }\n);\n```\n:::\n",
+ "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n# Control flow, structs, modules and types\n\nWe have discussed a lot of Zig's syntax in the last chapter,\nespecially in @sec-root-file and @sec-main-file.\nBut we still need to discuss some other very important\nelements of the language. Elements that you will use constantly on your day-to-day\nroutine.\n\nWe begin this chapter by discussing the different keywords and structures\nin Zig related to control flow (e.g. loops and if statements).\nThen, we talk about structs and how they can be used to do some\nbasic Object-Oriented (OOP) patterns in Zig. We also talk about\ntype inference and type casting.\nFinally, we end this chapter by discussing modules, and how they relate\nto structs.\n\n\n\n## Control flow {#sec-zig-control-flow}\n\nSometimes, you need to make decisions in your program. Maybe you need to decide\nwhether or not to execute a specific piece of code. Or maybe,\nyou need to apply the same operation over a sequence of values. These kinds of tasks,\ninvolve using structures that are capable of changing the \"control flow\" of our program.\n\nIn computer science, the term \"control flow\" usually refers to the order in which expressions (or commands)\nare evaluated in a given language or program. But this term is also used to refer\nto structures that are capable of changing this \"evaluation order\" of the commands\nexecuted by a given language/program.\n\nThese structures are better known\nby a set of terms, such as: loops, if/else statements, switch statements, among others. So,\nloops and if/else statements are examples of structures that can change the \"control\nflow\" of our program. The keywords `continue` and `break` are also examples of symbols\nthat can change the order of evaluation, since they can move our program to the next iteration\nof a loop, or make the loop stop completely.\n\n\n### If/else statements\n\nAn if/else statement performs a \"conditional flow operation\".\nA conditional flow control (or choice control) allows you to execute\nor ignore a certain block of commands based on a logical condition.\nMany programmers and computer science professionals also use\nthe term \"branching\" in this case.\nIn essence, an if/else statement allow us to use the result of a logical test\nto decide whether or not to execute a given block of commands.\n\nIn Zig, we write if/else statements by using the keywords `if` and `else`.\nWe start with the `if` keyword followed by a logical test inside a pair\nof parentheses, followed by a pair of curly braces which contains the lines\nof code to be executed in case the logical test returns the value `true`.\n\nAfter that, you can optionally add an `else` statement. To do that, just add the `else`\nkeyword followed by a pair of curly braces, with the lines of code\nto executed in case the logical test defined at `if` returns `false`.\n\nIn the example below, we are testing if the object `x` contains a number\nthat is greater than 10. Judging by the output printed to the console,\nwe know that this logical test returned `false`. Because the output\nin the console is compatible with the line of code present in the\n`else` branch of the if/else statement.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst x = 5;\nif (x > 10) {\n try stdout.print(\n \"x > 10!\\n\", .{}\n );\n} else {\n try stdout.print(\n \"x <= 10!\\n\", .{}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nx <= 10!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Switch statements {#sec-switch}\n\nSwitch statements are also available in Zig, and they have a very similar syntax to a switch statement in Rust.\nAs you would expect, to write a switch statement in Zig we use the `switch` keyword.\nWe provide the value that we want to \"switch over\" inside a\npair of parentheses. Then, we list the possible combinations (or \"branches\")\ninside a pair of curly braces.\n\nLet's take a look at the code example below. You can see that\nI'm creating an enum type called `Role`. We talk more about enums in @sec-enum.\nBut in summary, this `Role` type is listing different types of roles in a fictitious\ncompany, like `SE` for Software Engineer, `DE` for Data Engineer, `PM` for Product Manager,\netc.\n\nNotice that we are using the value from the `role` object in the\nswitch statement, to discover which exact area we need to store in the `area` variable object.\nAlso notice that we are using type inference inside the switch statement, with the dot character,\nas we are going to describe in @sec-type-inference.\nThis makes the `zig` compiler infer the correct data type of the values (`PM`, `SE`, etc.) for us.\n\nAlso notice that, we are grouping multiple values in the same branch of the switch statement.\nWe just separate each possible value with a comma. For example, if `role` contains either `DE` or `DA`,\nthe `area` variable would contain the value `\"Data & Analytics\"`, instead of `\"Platform\"` or `\"Sales\"`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst Role = enum {\n SE, DPE, DE, DA, PM, PO, KS\n};\n\npub fn main() !void {\n var area: []const u8 = undefined;\n const role = Role.SE;\n switch (role) {\n .PM, .SE, .DPE, .PO => {\n area = \"Platform\";\n },\n .DE, .DA => {\n area = \"Data & Analytics\";\n },\n .KS => {\n area = \"Sales\";\n },\n }\n try stdout.print(\"{s}\\n\", .{area});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nPlatform\n```\n\n\n:::\n:::\n\n\n\n\n\n#### Switch statements must exhaust all possibilities\n\nOne very important aspect about switch statements in Zig\nis that they must exhaust all existing possibilities.\nIn other words, all possible values that could be found inside the `role`\nobject must be explicitly handled in this switch statement.\n\nSince the `role` object has type `Role`, the only possible values to\nbe found inside this object are `PM`, `SE`, `DPE`, `PO`, `DE`, `DA` and `KS`.\nThere are no other possible values to be stored in this `role` object.\nThus, the switch statements must have a combination (branch) for each one of these values.\nThis is what \"exhaust all existing possibilities\" means. The switch statement covers\nevery possible case.\n\nTherefore, you cannot write a switch statement in Zig, and leave an edge case\nwith no explicit action to be taken.\nThis is a similar behaviour to switch statements in Rust, which also have to\nhandle all possible cases.\n\n\n\n#### The else branch\n\nTake a look at the `dump_hex_fallible()` function below as an example. This function\ncomes from the Zig Standard Library. More precisely, from the\n[`debug.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/debug.zig)[^debug-mod].\nThere are multiple lines in this function, but I omitted them to focus solely on the\nswitch statement found in this function. Notice that this switch statement has four\npossible cases (i.e. four explicit branches). Also, notice that we used an `else` branch\nin this case.\n\nAn `else` branch in a switch statement works as the \"default branch\".\nWhenever you have multiple cases in your switch statement where\nyou want to apply the exact same action, you can use an `else` branch to do that.\n\n[^debug-mod]: \n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub fn dump_hex_fallible(bytes: []const u8) !void {\n // Many lines ...\n switch (byte) {\n '\\n' => try writer.writeAll(\"␊\"),\n '\\r' => try writer.writeAll(\"␍\"),\n '\\t' => try writer.writeAll(\"␉\"),\n else => try writer.writeByte('.'),\n }\n}\n```\n:::\n\n\n\n\nMany programmers would also use an `else` branch to handle a \"not supported\" case.\nThat is, a case that cannot be properly handled by your code, or, just a case that\nshould not be \"fixed\". Therefore, you can use an `else` branch to panic (or raise an error)\nin your program to stop the current execution.\n\nTake the code example below. We can see that, we are handling the cases\nfor the `level` object being either 1, 2, or 3. All other possible cases are not supported by default,\nand, as consequence, we raise a runtime error in such cases through the `@panic()` built-in function.\n\nAlso notice that, we are assigning the result of the switch statement to a new object called `category`.\nThis is another thing that you can do with switch statements in Zig. If a branch\noutputs a value as result, you can store the result value of the switch statement into\na new object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 1, 2 => \"beginner\",\n 3 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n:::\n\n\n\n\n```\nthread 13103 panic: Not supported level!\nt.zig:9:13: 0x1033c58 in main (switch2)\n @panic(\"Not supported level!\");\n ^\n```\n\n\n\n#### Using ranges in switch\n\nFurthermore, you can also use ranges of values in switch statements.\nThat is, you can create a branch in your switch statement that is used\nwhenever the input value is within the specified range. These \"range expressions\"\nare created with the operator `...`. It is important\nto emphasize that the ranges created by this operator are\ninclusive on both ends.\n\nFor example, I could easily change the previous code example to support all\nlevels between 0 and 100. Like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst level: u8 = 4;\nconst category = switch (level) {\n 0...25 => \"beginner\",\n 26...75 => \"intermediary\",\n 76...100 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nbeginner\n```\n\n\n:::\n:::\n\n\n\n\nThis is neat, and it works with character ranges too. That is, I could\nsimply write `'a'...'z'`, to match any character value that is a\nlowercase letter, and it would work fine.\n\n\n#### Labeled switch statements\n\nIn @sec-blocks we have talked about labeling blocks, and also, about using these labels\nto return a value from the block. Well, from version 0.14.0 and onwards of the `zig` compiler,\nyou can also apply labels over switch statements, which makes it possible to almost implement a\n\"C `goto`\" like pattern.\n\nFor example, if you give the label `xsw` to a switch statement, you can use this\nlabel in conjunction with the `continue` keyword to go back to the beginning of the switch\nstatement. In the example below, the execution goes back to the beginning of the\nswitch statement two times, before ending at the `3` branch.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nxsw: switch (@as(u8, 1)) {\n 1 => {\n try stdout.print(\"First branch\\n\", .{});\n continue :xsw 2;\n },\n 2 => continue :xsw 3,\n 3 => return,\n 4 => {},\n else => {\n try stdout.print(\n \"Unmatched case, value: {d}\\n\", .{@as(u8, 1)}\n );\n },\n}\n```\n:::\n\n\n\n\n\n### The `defer` keyword {#sec-defer}\n\nWith the `defer` keyword you can register an expression to be executed when you exit the current scope.\nTherefore, this keyword has a similar functionality as the `on.exit()` function from R.\nTake the `foo()` function below as an example. When we execute this `foo()` function, the expression\nthat prints the message \"Exiting function ...\" is getting executed only when the function exits\nits scope.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn foo() !void {\n defer std.debug.print(\n \"Exiting function ...\\n\", .{}\n );\n try stdout.print(\"Adding some numbers ...\\n\", .{});\n const x = 2 + 2; _ = x;\n try stdout.print(\"Multiplying ...\\n\", .{});\n const y = 2 * 8; _ = y;\n}\n\npub fn main() !void {\n try foo();\n}\n```\n:::\n\n\n\n\n```\nAdding some numbers ...\nMultiplying ...\nExiting function ...\n```\n\nTherefore, we can use `defer` to declare an expression that is going to be executed\nwhen your code exits the current scope. Some programmers like to interpret the phrase \"exit of the current scope\"\nas \"the end of the current scope\". But this interpretation might not be entirely correct, depending\non what you consider as \"the end of the current scope\".\n\nI mean, what do you consider as **the end** of the current scope? Is it the closing curly bracket (`}`) of the scope?\nIs it when the last expression in the function get's executed? Is it when the function returns to the previous scope?\nEtc. For example, it would not be correct to interpret the \"exit of the current scope\" as the closing\ncurly bracket of the scope. Because the function might exit from an earlier position than this\nclosing curly bracket (e.g. an error value was generated at a previous line inside the function;\nthe function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.\n\nNow, if you remember of what we have discussed in @sec-blocks, there are multiple structures in the language\nthat create their own separate scopes. For/while loops, if/else statements,\nfunctions, normal blocks, etc. This also affects the interpretation of `defer`.\nFor example, if you use `defer` inside a for loop, then, the given expression\nwill be executed everytime this specific for loop exits its own scope.\n\nBefore we continue, is worth emphasizing that the `defer` keyword is an \"unconditional defer\".\nWhich means that the given expression will be executed no matter how the code exits\nthe current scope. For example, your code might exit the current scope because of an error value\nbeing generated, or, because of a return statement, or, a break statement, etc.\n\n\n\n### The `errdefer` keyword {#sec-errdefer1}\n\nOn the previous section, we have discussed the `defer` keyword, which you can use to\nregister an expression to be executed at the exit of the current scope.\nBut this keyword has a brother, which is the `errdefer` keyword. While `defer`\nis an \"unconditional defer\", the `errdefer` keyword is a \"conditional defer\".\nWhich means that the given expression is executed only when you exit the current\nscope on a very specific circumstance.\n\nIn more details, the expression given to `errdefer` is executed only when an error occurs in the current scope.\nTherefore, if the function (or for/while loop, if/else statement, etc.) exits the current scope\nin a normal situation, without errors, the expression given to `errdefer` is not executed.\n\nThis makes the `errdefer` keyword one of the many tools available in Zig for error handling.\nIn this section, we are more concerned with the control flow aspects around `errdefer`.\nBut we are going to discuss `errdefer` later as a error handling tool in @sec-errdefer2.\n\nThe code example below demonstrates three things:\n\n- that `defer` is an \"unconditional defer\", because the given expression get's executed regardless of how the function `foo()` exits its own scope.\n- that `errdefer` is executed because the function `foo()` returned an error value.\n- that `defer` and `errdefer` expressions are executed in a LIFO (*last in, first out*) order.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n defer i = 2;\n try foo();\n}\n```\n:::\n\n\n\n\n```\nValue of i: 2\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\nWhen I say that \"defer expressions\" are executed in a LIFO order, what I want to say is that\nthe last `defer` or `errdefer` expressions in the code are the first ones to be executed.\nYou could also interpret this as: \"defer expressions\" are executed from bottom to top, or,\nfrom last to first.\n\nTherefore, if I change the order of the `defer` and `errdefer` expressions, you will notice that\nthe value of `i` that get's printed to the console changes to 1. This doesn't mean that the\n`defer` expression was not executed in this case. This actually means that the `defer` expression\nwas executed only after the `errdefer` expression. The code example below demonstrates this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n defer i = 2;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n try foo();\n}\n```\n:::\n\n\n\n\n```\nValue of i: 1\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n```\n\n\n\n\n### For loops\n\nA loop allows you to execute the same lines of code multiple times,\nthus, creating a \"repetition space\" in the execution flow of your program.\nLoops are particularly useful when we want to replicate the same function\n(or the same set of commands) over different inputs.\n\nThere are different types of loops available in Zig. But the most\nessential of them all is probably the *for loop*. A for loop is\nused to apply the same piece of code over the elements of a slice, or, an array.\n\nFor loops in Zig use a syntax that may be unfamiliar to programmers coming from\nother languages. You start with the `for` keyword, then, you\nlist the items that you want to iterate\nover inside a pair of parentheses. Then, inside of a pair of pipes (`|`)\nyou should declare an identifier that will serve as your iterator, or,\nthe \"repetition index of the loop\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (items) |value| {\n // code to execute\n}\n```\n:::\n\n\n\n\nTherefore, instead of using a `(value in items)` syntax,\nin Zig, for loops use the syntax `(items) |value|`. In the example\nbelow, you can see that we are looping through the items\nof the array stored at the object `name`, and printing to the\nconsole the decimal representation of each character in this array.\n\nIf we wanted, we could also iterate through a slice (or a portion) of\nthe array, instead of iterating through the entire array stored in the `name` object.\nJust use a range selector to select the section you want. For example,\nI could provide the expression `name[0..3]` to the for loop, to iterate\njust through the first 3 elements in the array.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name = [_]u8{'P','e','d','r','o'};\nfor (name) |char| {\n try stdout.print(\"{d} | \", .{char});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n80 | 101 | 100 | 114 | 111 | \n```\n\n\n:::\n:::\n\n\n\n\nIn the above example we are using the value itself of each\nelement in the array as our iterator. But there are many situations where\nwe need to use an index instead of the actual values of the items.\n\nYou can do that by providing a second set of items to iterate over.\nMore precisely, you provide the range selector `0..` to the for loop. So,\nyes, you can use two different iterators at the same time in a for\nloop in Zig.\n\nBut remember from @sec-assignments that, every object\nyou create in Zig must be used in some way. So if you declare two iterators\nin your for loop, you must use both iterators inside the for loop body.\nBut if you want to use just the index iterator, and not use the \"value iterator\",\nthen, you can discard the value iterator by maching the\nvalue items to the underscore character, like in the example below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name = \"Pedro\";\nfor (name, 0..) |_, i| {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n:::\n\n\n\n\n```\n0 | 1 | 2 | 3 | 4 |\n```\n\n\n### While loops\n\nA while loop is created from the `while` keyword. A `for` loop\niterates through the items of an array, but a `while` loop\nwill loop continuously, and infinitely, until a logical test\n(specified by you) becomes false.\n\nYou start with the `while` keyword, then, you define a logical\nexpression inside a pair of parentheses, and the body of the\nloop is provided inside a pair of curly braces, like in the example below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) {\n try stdout.print(\"{d} | \", .{i});\n i += 1;\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\nYou can also specify the increment expression to be used at the beginning of a while loop.\nTo do that, we write the increment expression inside a pair of parentheses after a colon character (`:`).\nThe code example below demonstrates this other pattern.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: u8 = 1;\nwhile (i < 5) : (i += 1) {\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 2 | 3 | 4 | \n```\n\n\n:::\n:::\n\n\n\n\n### Using `break` and `continue`\n\nIn Zig, you can explicitly stop the execution of a loop, or, jump to the next iteration of the loop, by using\nthe keywords `break` and `continue`, respectively. The `while` loop presented in the next code example is,\nat first sight, an infinite loop. Because the logical value inside the parenthese will always be equal to `true`.\nBut what makes this `while` loop stop when the `i` object reaches the count\n10? It is the `break` keyword!\n\nInside the while loop, we have an if statement that is constantly checking if the `i` variable\nis equal to 10. Since we are incrementing the value of `i` at each iteration of the\nwhile loop, this `i` object will eventually be equal to 10, and when it is, the if statement\nwill execute the `break` expression, and, as a result, the execution of the while loop is stopped.\n\nNotice the use of the `expect()` function from the Zig Standard Library after the while loop.\nThis `expect()` function is an \"assert\" type of function.\nThis function checks if the logical test provided is equal to true. If so, the function do nothing.\nOtherwise (i.e. the logical test is equal to false), the function raises an assertion error.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar i: usize = 0;\nwhile (true) {\n if (i == 10) {\n break;\n }\n i += 1;\n}\ntry std.testing.expect(i == 10);\ntry stdout.print(\"Everything worked!\", .{});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nEverything worked!\n```\n\n\n:::\n:::\n\n\n\n\nSince this code example was executed successfully by the `zig` compiler,\nwithout raising any errors, we known that, after the execution of the while loop,\nthe `i` object is equal to 10. Because if it wasn't equal to 10, an error would have\nbeen raised by `expect()`.\n\nNow, in the next example, we have a use case for\nthe `continue` keyword. The if statement is constantly\nchecking if the current index is a multiple of 2. If\nit is, we jump to the next iteration of the loop.\nOtherwise, the loop just prints the current index to the console.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ns = [_]u8{1,2,3,4,5,6};\nfor (ns) |i| {\n if ((i % 2) == 0) {\n continue;\n }\n try stdout.print(\"{d} | \", .{i});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1 | 3 | 5 | \n```\n\n\n:::\n:::\n\n\n\n\n\n\n## Function parameters are immutable {#sec-fun-pars}\n\nWe have already discussed a lot of the syntax behind function declarations in @sec-root-file and @sec-main-file.\nBut I want to emphasize a curious fact about function parameters (a.k.a. function arguments) in Zig.\nIn summary, function parameters are immutable in Zig.\n\nTake the code example below, where we declare a simple function that just tries to add\nsome amount to the input integer, and returns the result back. If you look closely\nat the body of this `add2()` function, you will notice that we try\nto save the result back into the `x` function argument.\n\nIn other words, this function not only uses the value that it received through the function argument\n`x`, but it also tries to change the value of this function argument, by assigning the addition result\ninto `x`. However, function arguments in Zig are immutable. You cannot change their values, or, you\ncannot assign values to them inside the body's function.\n\nThis is the reason why, the code example below does not compile successfully. If you try to compile\nthis code example, you will get a compile error message about \"trying to change the value of a\nimmutable (i.e. constant) object\".\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: u32) u32 {\n x = x + 2;\n return x;\n}\n\npub fn main() !void {\n const y = add2(4);\n std.debug.print(\"{d}\\n\", .{y});\n}\n```\n:::\n\n\n\n\n```\nt.zig:3:5: error: cannot assign to constant\n x = x + 2;\n ^\n```\n\n\n### A free optimization\n\nIf a function argument receives as input an object whose data type is\nany of the primitive types that we have listed in @sec-primitive-data-types,\nthis object is always passed by value to the function. In other words, this object\nis copied into the function stack frame.\n\nHowever, if the input object have a more complex data type, for example, it might\nbe a struct instance, or an array, or an union value, etc., in cases like that, the `zig` compiler\nwill take the liberty of deciding for you which strategy is best. Thus, the `zig` compiler will\npass your object to the function either by value, or by reference. The compiler will always\nchoose the strategy that is faster for you.\nThis optimization that you get for free is possible only because function arguments are\nimmutable in Zig.\n\n\n### How to overcome this barrier\n\nThere are some situations where you might need to change the value of your function argument\ndirectly inside the function's body. This happens more often when we are passing\nC structs as inputs to Zig functions.\n\nIn a situation like this, you can overcome this barrier by using a pointer. In other words,\ninstead of passing a value as input to the argument, you can pass a \"pointer to value\" instead.\nYou can change the value that the pointer points to, by dereferencing it.\n\nTherefore, if we take our previous `add2()` example, we can change the value of the\nfunction argument `x` inside the function's body by marking the `x` argument as a\n\"pointer to a `u32` value\" (i.e. `*u32` data type), instead of a `u32` value.\nBy making it a pointer, we can finally alter the value of this function argument directly inside\nthe body of the `add2()` function. You can see that the code example below compiles successfully.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn add2(x: *u32) void {\n const d: u32 = 2;\n x.* = x.* + d;\n}\n\npub fn main() !void {\n var x: u32 = 4;\n add2(&x);\n std.debug.print(\"Result: {d}\\n\", .{x});\n}\n```\n:::\n\n\n\n\n```\nResult: 6\n```\n\n\nEven in this code example above, the `x` argument is still immutable. Which means that the pointer itself is immutable.\nTherefore, you cannot change the memory address that it points to. However, you can dereference the pointer\nto access the value that it points to, and also, to change this value, if you need to.\n\n\n\n\n\n## Structs and OOP {#sec-structs-and-oop}\n\nZig is a language more closely related to C (which is a procedural language),\nthan it is to C++ or Java (which are object-oriented languages). Because of that, you do not\nhave advanced OOP (Object-Oriented Programming) patterns available in Zig, such as classes, interfaces or\nclass inheritance. Nonetheless, OOP in Zig is still possible by using struct definitions.\n\nWith struct definitions, you can create (or define) a new data type in Zig. These struct definitions work the same way as they work in C.\nYou give a name to this new struct (or, to this new data type you are creating), then, you list the data members of this new struct. You can\nalso register functions inside this struct, and they become the methods of this particular struct (or data type), so that, every object\nthat you create with this new type, will always have these methods available and associated with them.\n\nIn C++, when we create a new class, we normally have a constructor method (or, a constructor function) which\nis used to construct (or, to instantiate) every object of this particular class, and we also have\na destructor method (or a destructor function), which is the function responsible for destroying\nevery object of this class.\n\nIn Zig, we normally declare the constructor and the destructor methods\nof our structs, by declaring an `init()` and a `deinit()` methods inside the struct.\nThis is just a naming convention that you will find across the entire Zig Standard Library.\nSo, in Zig, the `init()` method of a struct is normally the constructor method of the class represented by this struct.\nWhile the `deinit()` method is the method used for destroying an existing instance of that struct.\n\nThe `init()` and `deinit()` methods are both used extensively in Zig code, and you will see both of\nthem being used when we talk about allocators in @sec-allocators.\nBut, as another example, let's build a simple `User` struct to represent a user of some sort of system.\n\nIf you look at the `User` struct below, you can see the `struct` keyword.\nNotice the data members of this struct: `id`, `name` and `email`. Every data member has its\ntype explicitly annotated, with the colon character (`:`) syntax that we described earlier in @sec-root-file.\nBut also notice that every line in the struct body that describes a data member, ends with a comma character (`,`).\nSo every time you declare a data member in your Zig code, always end the line with a comma character, instead\nof ending it with the traditional semicolon character (`;`).\n\nNext, we have registered an `init()` function as a method\nof this `User` struct. This `init()` method is the constructor method that we will use to instantiate\nevery new `User` object. That is why this `init()` function returns a new `User` object as result.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst User = struct {\n id: u64,\n name: []const u8,\n email: []const u8,\n\n pub fn init(id: u64,\n name: []const u8,\n email: []const u8) User {\n\n return User {\n .id = id,\n .name = name,\n .email = email\n };\n }\n\n pub fn print_name(self: User) !void {\n try stdout.print(\"{s}\\n\", .{self.name});\n }\n};\n\npub fn main() !void {\n const u = User.init(1, \"pedro\", \"email@gmail.com\");\n try u.print_name();\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\npedro\n```\n\n\n:::\n:::\n\n\n\n\nThe `pub` keyword plays an important role in struct declarations, and OOP in Zig.\nEvery method that you declare in your struct that is marked with the keyword `pub`,\nbecomes a public method of this particular struct.\n\nSo every method that you create inside your struct, is, at first, a private method\nof that struct. Meaning that, this method can only be called from within this\nstruct. But, if you mark this method as public, with the keyword `pub`, then,\nyou can call the method directly from an instance of the `User` struct.\n\nIn other words, the functions marked by the keyword `pub`\nare members of the public API of that struct.\nFor example, if I did not mark the `print_name()` method as public,\nthen, I could not execute the line `u.print_name()`. Because I would\nnot be authorized to call this method directly in my code.\n\n\n\n### Anonymous struct literals {#sec-anonymous-struct-literals}\n\nYou can declare a struct object as a literal value. When we do that, we normally specify the\ndata type of this struct literal by writing its data type just before the opening curly brace.\nFor example, I could write a struct literal value of the type `User` that we have defined\nin the previous section like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst eu = User {\n .id = 1,\n .name = \"Pedro\",\n .email = \"someemail@gmail.com\"\n};\n_ = eu;\n```\n:::\n\n\n\n\nHowever, in Zig, we can also write an anonymous struct literal. That is, you can write a\nstruct literal, but not specify explicitly the type of this particular struct.\nAn anonymous struct is written by using the syntax `.{}`. So, we essentially\nreplaced the explicit type of the struct literal with a dot character (`.`).\n\nAs we described in @sec-type-inference, when you put a dot before a struct literal,\nthe type of this struct literal is automatically inferred by the `zig` compiler.\nIn essence, the `zig` compiler will look for some hint of what is the type of that struct.\nThis hint can be the type annotation of a function argument,\nor the return type annotation of the function that you are using, or the type annotation\nof an existing object.\nIf the compiler does find such type annotation, it will use this\ntype in your literal struct.\n\nAnonymous structs are very commonly used as inputs to function arguments in Zig.\nOne example that you have seen already constantly, is the `print()`\nfunction from the `stdout` object.\nThis function takes two arguments.\nThe first argument, is a template string, which should\ncontain string format specifiers in it, which tells how the values provided\nin the second argument should be printed into the message.\n\nWhile the second argument is a struct literal that lists the values\nto be printed into the template message specified in the first argument.\nYou normally want to use an anonymous struct literal here, so that the\n`zig` compiler do the job of specifying the type of this particular\nanonymous struct for you.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const stdout = std.io.getStdOut().writer();\n try stdout.print(\"Hello, {s}!\\n\", .{\"world\"});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nHello, world!\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Struct declarations must be constant\n\nTypes in Zig must be `const` or `comptime` (we are going to talk more about comptime in @sec-comptime).\nWhat this means is that you cannot create a new data type, and mark it as variable with the `var` keyword.\nSo struct declarations are always constant. You cannot declare a new struct type using the `var` keyword.\nIt must be `const`.\n\nIn the `Vec3` example below, this declaration is allowed because I'm using the `const` keyword\nto declare this new data type.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n};\n```\n:::\n\n\n\n\n\n### The `self` method argument {#sec-self-arg}\n\nIn every language that have OOP, when we declare a method of some class or struct, we\nusually declare this method as a function that has a `self` argument.\nThis `self` argument is the reference to the object itself from which the method\nis being called from.\n\nIt is not mandatory to use this `self` argument. But why would you not use this `self` argument?\nThere is no reason to not use it. Because the only way to get access to the data stored in the\ndata members of your struct is to access them through this `self` argument.\nIf you don't need to use the data in the data members of your struct inside your method, you very likely don't need\na method. You can just declare this logic as a simple function, outside of your\nstruct declaration.\n\n\nTake the `Vec3` struct below. Inside this `Vec3` struct we declared a method named `distance()`.\nThis method calculates the distance between two `Vec3` objects, by following the distance\nformula in euclidean space. Notice that this `distance()` method takes two `Vec3` objects\nas input, `self` and `other`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n};\n```\n:::\n\n\n\n\n\nThe `self` argument corresponds to the `Vec3` object from which this `distance()` method\nis being called from. While the `other` is a separate `Vec3` object that is given as input\nto this method. In the example below, the `self` argument corresponds to the object\n`v1`, because the `distance()` method is being called from the `v1` object,\nwhile the `other` argument corresponds to the object `v2`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst v1 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nconst v2 = Vec3 {\n .x = 5.1, .y = 5.6, .z = 1.6\n};\n\nstd.debug.print(\n \"Distance: {d}\\n\",\n .{v1.distance(v2)}\n);\n```\n:::\n\n\n\n\n```\nDistance: 3.3970575502926055\n```\n\n\n\n### About the struct state\n\nSometimes you don't need to care about the state of your struct object. Sometimes, you just need\nto instantiate and use the objects, without altering their state. You can notice that when you have methods\ninside your struct declaration that might use the values that are present in the data members, but they\ndo not alter the values in these data members of the struct in anyway.\n\nThe `Vec3` struct that was presented in @sec-self-arg is an example of that.\nThis struct have a single method named `distance()`, and this method does use the values\npresent in all three data members of the struct (`x`, `y` and `z`). But at the same time,\nthis method does not change the values of these data members at any point.\n\nAs a result of that, when we create `Vec3` objects we usually create them as\nconstant objects, like the `v1` and `v2` objects presented in @sec-self-arg.\nWe can create them as variable objects with the `var` keyword,\nif we want to. But because the methods of this `Vec3` struct do not change\nthe state of the objects in any point, it's unnecessary to mark them\nas variable objects.\n\nBut why? Why am I talking about this here? It's because the `self` argument\nin the methods is affected depending on whether the\nmethods present in a struct change or don't change the state of the object itself.\nMore specifically, when you have a method in a struct that changes the state\nof the object (i.e. change the value of a data member), the `self` argument\nin this method must be annotated in a different manner.\n\nAs I described in @sec-self-arg, the `self` argument in methods of\na struct is the argument that receives as input the object from which the method\nwas called from. We usually annotate this argument in the methods by writing `self`,\nfollowed by the colon character (`:`), and the data type of the struct to which\nthe method belongs to (e.g. `User`, `Vec3`, etc.).\n\nIf we take the `Vec3` struct that we defined in the previous section as an example,\nwe can see in the `distance()` method that this `self` argument is annotated as\n`self: Vec3`. Because the state of the `Vec3` object is never altered by this\nmethod.\n\nBut what if we do have a method that alters the state of the object, by altering the\nvalues of its data members, how should we annotate `self` in this instance? The answer is:\n\"we should annotate `self` as a pointer of `x`, instead of just `x`\".\nIn other words, you should annotate `self` as `self: *x`, instead of annotating it\nas `self: x`.\n\nIf we create a new method inside the `Vec3` object that, for example, expands the\nvector by multiplying its coordinates by a factor of two, then, we need to follow\nthis rule specified in the previous paragraph. The code example below demonstrates\nthis idea:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst m = std.math;\nconst Vec3 = struct {\n x: f64,\n y: f64,\n z: f64,\n\n pub fn distance(self: Vec3, other: Vec3) f64 {\n const xd = m.pow(f64, self.x - other.x, 2.0);\n const yd = m.pow(f64, self.y - other.y, 2.0);\n const zd = m.pow(f64, self.z - other.z, 2.0);\n return m.sqrt(xd + yd + zd);\n }\n\n pub fn twice(self: *Vec3) void {\n self.x = self.x * 2.0;\n self.y = self.y * 2.0;\n self.z = self.z * 2.0;\n }\n};\n```\n:::\n\n\n\n\nNotice in the code example above that we have added a new method\nto our `Vec3` struct named `twice()`. This method doubles the\ncoordinate values of our vector object. In the\ncase of the `twice()` method, we annotated the `self` argument as `*Vec3`,\nindicating that this argument receives a pointer (or a reference, if you prefer to call it this way)\nto a `Vec3` object as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar v3 = Vec3 {\n .x = 4.2, .y = 2.4, .z = 0.9\n};\nv3.twice();\nstd.debug.print(\"Doubled: {d}\\n\", .{v3.x});\n```\n:::\n\n\n\n\n```\nDoubled: 8.4\n```\n\n\n\nNow, if you change the `self` argument in this `twice()` method to `self: Vec3`, like in the\n`distance()` method, you will get the compiler error exposed below as result. Notice that this\nerror message is showing a line from the `twice()` method body,\nindicating that you cannot alter the value of the `x` data member.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\n// If we change the function signature of double to:\n pub fn twice(self: Vec3) void {\n```\n:::\n\n\n\n\n```\nt.zig:16:13: error: cannot assign to constant\n self.x = self.x * 2.0;\n ~~~~^~\n```\n\nThis error message indicates that the `x` data member belongs to a constant object,\nand, because of that, it cannot be changed. Ultimately, this error message\nis telling us that the `self` argument is constant.\n\nIf you take some time, and think hard about this error message, you will understand it.\nYou already have the tools to understand why we are getting this error message.\nWe have talked about it already in @sec-fun-pars.\nSo remember, every function argument is immutable in Zig, and `self`\nis no exception to this rule.\n\nIn this example, we marked the `v3` object as a variable object.\nBut this does not matter. Because it is not about the input object, it is about\nthe function argument.\n\nThe problem begins when we try to alter the value of `self` directly, which is a function argument,\nand, every function argument is immutable by default. You may ask yourself how can we overcome\nthis barrier, and once again, the solution was also discussed in @sec-fun-pars.\nWe overcome this barrier, by explicitly marking the `self` argument as a pointer.\n\n\n::: {.callout-note}\nIf a method of your `x` struct alters the state of the object, by\nchanging the value of any data member, then, remember to use `self: *x`,\ninstead of `self: x` in the function signature of this method.\n:::\n\n\nYou could also interpret the content discussed in this section as:\n\"if you need to alter the state of your `x` struct object in one of its methods,\nyou must explicitly pass the `x` struct object by reference to the `self` argument of this method\".\n\n\n\n## Type inference {#sec-type-inference}\n\nZig is a strongly typed language. But, there are some situations\nwhere you don't have to explicitly write the type of every single object in your source code,\nas you would expect from a traditional strongly typed language, such as C and C++.\n\nIn some situations, the `zig` compiler can use type inference to solve the data types for you, easing some of\nthe burden that you carry as a developer.\nThe most common way this happens is through function arguments that receive struct objects\nas input.\n\nIn general, type inference in Zig is done by using the dot character (`.`).\nEverytime you see a dot character written before a struct literal, or before an enum value, or something like that,\nyou know that this dot character is playing a special party in this place. More specifically, it is\ntelling the `zig` compiler something along the lines of: \"Hey! Can you infer the type of this\nvalue for me? Please!\". In other words, this dot character is playing a similar role as the `auto` keyword in C++.\n\nI gave you some examples of this in @sec-anonymous-struct-literals, where we used anonymous struct literals.\nAnonymous struct literals are, struct literals that use type inference to\ninfer the exact type of this particular struct literal.\nThis type inference is done by looking for some minimal hint of the correct data type to be used.\nYou could say that the `zig` compiler looks for any neighbouring type annotation that might tell it\nwhat the correct type would be.\n\nAnother common place where we use type inference in Zig is at switch statements (which we talked about in @sec-switch).\nI also gave some other examples of type inference in @sec-switch, where we were inferring the data types of enum values listed inside\nof switch statements (e.g. `.DE`).\nBut as another example, take a look at this `fence()` function reproduced below,\nwhich comes from the [`atomic.zig` module](https://github.com/ziglang/zig/blob/master/lib/std/atomic.zig)[^fence-fn]\nof the Zig Standard Library.\n\n[^fence-fn]: .\n\nThere are a lot of things in this function that we haven't talked about yet, such as:\nwhat `comptime` means? `inline`? `extern`?\nLet's just ignore all of these things, and focus solely on the switch statement\nthat is inside this function.\n\nWe can see that this switch statement uses the `order` object as input. This `order`\nobject is one of the inputs of this `fence()` function, and we can see in the type annotation,\nthat this object is of type `AtomicOrder`. We can also see a bunch of values inside the\nswitch statements that begin with a dot character, such as `.release` and `.acquire`.\n\nBecause these weird values contain a dot character before them, we are asking the `zig`\ncompiler to infer the types of these values inside the switch statement. Then, the `zig`\ncompiler is looking into the current context where these values are being used, and it is\ntrying to infer the types of these values.\n\nSince they are being used inside a switch statement, the `zig` compiler looks into the type\nof the input object given to the switch statement, which is the `order` object in this case.\nBecause this object have type `AtomicOrder`, the `zig` compiler infers that these values\nare data members from this type `AtomicOrder`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub inline fn fence(self: *Self, comptime order: AtomicOrder) void {\n // many lines of code ...\n if (builtin.sanitize_thread) {\n const tsan = struct {\n extern \"c\" fn __tsan_acquire(addr: *anyopaque) void;\n extern \"c\" fn __tsan_release(addr: *anyopaque) void;\n };\n\n const addr: *anyopaque = self;\n return switch (order) {\n .unordered, .monotonic => @compileError(\n @tagName(order)\n ++ \" only applies to atomic loads and stores\"\n ),\n .acquire => tsan.__tsan_acquire(addr),\n .release => tsan.__tsan_release(addr),\n .acq_rel, .seq_cst => {\n tsan.__tsan_acquire(addr);\n tsan.__tsan_release(addr);\n },\n };\n }\n\n return @fence(order);\n}\n```\n:::\n\n\n\n\nThis is how basic type inference is done in Zig. If we didn't use the dot character before\nthe values inside this switch statement, then, we would be forced to explicitly write\nthe data types of these values. For example, instead of writing `.release` we would have to\nwrite `AtomicOrder.release`. We would have to do this for every single value\nin this switch statement, and this is a lot of work. That is why type inference\nis commonly used on switch statements in Zig.\n\n\n\n## Type casting {#sec-type-cast}\n\nIn this section, I want to discuss type casting (or, type conversion) with you.\nWe use type casting when we have an object of type \"x\", and we want to convert\nit into an object of type \"y\", i.e. we want to change the data type of the object.\n\nMost languages have a formal way to perform type casting. In Rust for example, we normally\nuse the keyword `as`, and in C, we normally use the type casting syntax, e.g. `(int) x`.\nIn Zig, we use the `@as()` built-in function to cast an object of type \"x\", into\nan object of type \"y\".\n\nThis `@as()` function is the preferred way to perform type conversion (or type casting)\nin Zig. Because it is explicit, and, it also performs the casting only if it\nis unambiguous and safe. To use this function, you just provide the target data type\nin the first argument, and, the object that you want cast as the second argument.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 500;\n const y = @as(u32, x);\n try expect(@TypeOf(y) == u32);\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1/1 filecdc41797a754.test_0...OKAll 1 tests passed\n d.\n```\n\n\n:::\n:::\n\n\n\n\nThis is the general way to perform type casting in Zig. But remember, `@as()` works only when casting\nis unambiguous and safe, and there are situations where these assumptions do not hold. For example,\nwhen casting an integer value into a float value, or vice-versa, it is not clear to the compiler\nhow to perform this conversion safely.\n\nTherefore, we need to use specialized \"casting functions\" in such situations.\nFor example, if you want to cast an integer value into a float value, then, you\nshould use the `@floatFromInt()` function. In the inverse scenario, you should use\nthe `@intFromFloat()` function.\n\nIn these functions, you just provide the object that you want to\ncast as input. Then, the target data type of the \"type casting operation\" is determined by\nthe type annotation of the object where you are saving the results.\nIn the example below, we are casting the object `x` into a value of type `f32`,\nbecause the object `y`, which is where we are saving the results, is annotated\nas an object of type `f32`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 565;\n const y: f32 = @floatFromInt(x);\n try expect(@TypeOf(y) == f32);\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1/1 filecdc428638149.test_0...OKAll 1 tests passed\n d.\n```\n\n\n:::\n:::\n\n\n\n\nAnother built-in function that is very useful when performing type casting operations is `@ptrCast()`.\nIn essence, we use the `@as()` built-in function when we want to explicit convert (or cast) a Zig value/object\nfrom a type \"x\" to a type \"y\", etc. However, pointers (we are going to discuss pointers\nin more depth in @sec-pointer) are a special type of object in Zig,\ni.e. they are treated differently from \"normal objects\".\n\nEverytime a pointer is involved in some \"type casting operation\" in Zig, the `@ptrCast()` function is used.\nThis function works similarly to `@floatFromInt()`.\nYou just provide the pointer object that you want to cast as input to this function, and the\ntarget data type is, once again, determined by the type annotation of the object where the results are being\nstored.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const bytes align(@alignOf(u32)) = [_]u8{\n 0x12, 0x12, 0x12, 0x12\n };\n const u32_ptr: *const u32 = @ptrCast(&bytes);\n try expect(@TypeOf(u32_ptr) == *const u32);\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n1/1 filecdc46f71cc03.test_0...OKAll 1 tests passed\n d.\n```\n\n\n:::\n:::\n\n\n\n\n\n\n\n\n## Modules\n\nWe already talked about what modules are, and also, how to import other modules into\nyour current module via *import statements*. Every Zig module (i.e. a `.zig` file) that you write in your project\nis internally stored as a struct object. Take the line exposed below as an example. In this line we are importing the\nZig Standard Library into our current module.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n```\n:::\n\n\n\n\nWhen we want to access the functions and objects from the standard library, we\nare basically accessing the data members of the struct stored in the `std`\nobject. That is why we use the same syntax that we use in normal structs, with the dot operator (`.`)\nto access the data members and methods of the struct.\n\nWhen this \"import statement\" get's executed, the result of this expression is a struct\nobject that contains the Zig Standard Library modules, global variables, functions, etc.\nAnd this struct object get's saved (or stored) inside the constant object named `std`.\n\n\nTake the [`thread_pool.zig` module from the project `zap`](https://github.com/kprotty/zap/blob/blog/src/thread_pool.zig)[^thread]\nas an example. This module is written as if it was\na big struct. That is why we have a top-level and public `init()` method\nwritten in this module. The idea is that all top-level functions written in this\nmodule are methods from the struct, and all top-level objects and struct declarations\nare data members of this struct. The module is the struct itself.\n\n[^thread]: \n\n\nSo you would import and use this module by doing something like this:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst ThreadPool = @import(\"thread_pool.zig\");\nconst num_cpus = std.Thread.getCpuCount()\n catch @panic(\"failed to get cpu core count\");\nconst num_threads = std.math.cast(u16, num_cpus)\n catch std.math.maxInt(u16);\nconst pool = ThreadPool.init(\n .{ .max_threads = num_threads }\n);\n```\n:::\n",
"supporting": [
"03-structs_files"
],
diff --git a/docs/Chapters/01-zig-weird.html b/docs/Chapters/01-zig-weird.html
index 2f44cd2d..f7360c5b 100644
--- a/docs/Chapters/01-zig-weird.html
+++ b/docs/Chapters/01-zig-weird.html
@@ -400,7 +400,7 @@
-
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.
+
The init 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.
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.
@@ -567,7 +567,7 @@
On the other side, if you use var, then, you are creating a variable (or mutable) object. You can change the value of this object as many times you want. Using the keyword var in Zig is similar to using the keywords let mut in Rust.
1.4.1 Constant objects vs variable objects
-
In the code example below, we are creating a new constant object called age. This object stores a number representing the age of someone. However, this code example does not compiles successfully. Because on the next line of code, we are trying to change the value of the object age to 25.
+
In the code example below, we are creating a new constant object called age. This object stores a number representing the age of someone. However, this code example does not compile successfully. Because on the next line of code, we are trying to change the value of the object age to 25.
The zig compiler detects that we are trying to change the value of an object/identifier that is constant, and because of that, the compiler will raise a compilation error, warning us about the mistake.
const age = 24;
@@ -854,7 +854,7 @@
1.8.1 Strings in C
At first glance, a string literal value in Zig looks very similar to how C treats strings as well. In more details, string values in C are treated internally as an array of arbitrary bytes, and this array is also null-terminated.
-
But one key difference between a Zig string literal and a C string, is that Zig also stores the length of the string inside the object itself. In the case of a string literal value, this length is stored in the data type of the value (i.e. the n variable in [n:0]u8). While, in a string object, the length is stored in the len attribute of the slice that represents the string object. This small detail makes your code safer, because is much easier for the Zig compiler to check if you are trying to access an element that is “out of bounds”, i.e. if your trying to access memory that does not belong to you.
+
But one key difference between a Zig string literal and a C string, is that Zig also stores the length of the string inside the object itself. In the case of a string literal value, this length is stored in the data type of the value (i.e. the n variable in [n:0]u8). While, in a string object, the length is stored in the len attribute of the slice that represents the string object. This small detail makes your code safer, because it is much easier for the Zig compiler to check if you are trying to access an element that is “out of bounds”, i.e. if your trying to access memory that does not belong to you.
To achieve this same kind of safety in C, you have to do a lot of work that kind of seems pointless. So getting this kind of safety is not automatic and much harder to do in C. For example, if you want to track the length of your string throughout your program in C, then, you first need to loop through the array of bytes that represents this string, and find the null element ('\0') position to discover where exactly the array ends, or, in other words, to find how much elements the array of bytes contain.
To do that, you would need something like this in C. In this example, the C string stored in the object array is 25 bytes long:
@@ -1033,7 +1033,7 @@
As Richard Feldman explains on his most recent GOTO conference talk24 , we haven’t figured it out yet a way to achieve true safety in technology. In other words, we haven’t found a way to build software that won’t be exploited with 100% certainty. We can greatly reduce the risks of our software being exploited, by ensuring memory safety for example. But this is not enough to achieve “true safety” territory.
Because even if you write your program in a “safe language”, hackers can still exploit failures in the operating system where your program is running (e.g. maybe the system where your code is running has a “backdoor exploit” that can still affect your code in unexpected ways), or also, they can exploit the features from the architecture of your computer. A recently found exploit that involves memory invalidation through a feature of “memory tags” present in ARM chips is an example of that (Kim et al. 2024).
The question is: what have Zig and other languages been doing to mitigate this problem? If we take Rust as an example, Rust is, for the most part25, a memory safe language by enforcing specific rules to the developer. In other words, the key feature of Rust, the borrow checker, forces you to follow a specific logic when you are writing your Rust code, and the Rust compiler will always complain everytime you try to go out of this pattern.
-
In contrast, the Zig language is not a memory safe language by default. There are some memory safety features that you get for free in Zig, especially in arrays and pointer objects. But there are other tools offered by the language, that are not used by default. In other words, the zig compiler does not obligates you to use such tools.
+
In contrast, the Zig language is not a memory safe language by default. There are some memory safety features that you get for free in Zig, especially in arrays and pointer objects. But there are other tools offered by the language, that are not used by default. In other words, the zig compiler does not obligate you to use such tools.
The tools listed below are related to memory safety. That is, they help you to achieve memory safety in your Zig code:
defer allows you to keep free operations physically 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.
2.1.2.1 Switch statements must exhaust all possibilities
-
One very important aspect about switch statements in Zig is that they must exhaust all existing possibilities. In other words, all possible values that could be found inside the order object must be explicitly handled in this switch statement.
-
Since the role object have type Role, the only possible values to be found inside this object are PM, SE, DPE, PO, DE, DA and KS. There are no other possible values to be stored in this role object. Thus, the switch statements must have a combination (branch) for each one of these values. This is what “exhaust all existing possibilities” means. The switch statement covers every possible case.
+
One very important aspect about switch statements in Zig is that they must exhaust all existing possibilities. In other words, all possible values that could be found inside the role object must be explicitly handled in this switch statement.
+
Since the role object has type Role, the only possible values to be found inside this object are PM, SE, DPE, PO, DE, DA and KS. There are no other possible values to be stored in this role object. Thus, the switch statements must have a combination (branch) for each one of these values. This is what “exhaust all existing possibilities” means. The switch statement covers every possible case.
Therefore, you cannot write a switch statement in Zig, and leave an edge case with no explicit action to be taken. This is a similar behaviour to switch statements in Rust, which also have to handle all possible cases.
@@ -927,8 +927,8 @@
try expect(@TypeOf(y) == u32);}
-
1/1 file10b91234368fa.test_0...OKAll 1 tests passe
- ed.
+
1/1 filecdc41797a754.test_0...OKAll 1 tests passed
+ d.
This is the general way to perform type casting in Zig. But remember, @as() works only when casting is unambiguous and safe, and there are situations where these assumptions do not hold. For example, when casting an integer value into a float value, or vice-versa, it is not clear to the compiler how to perform this conversion safely.
@@ -943,8 +943,8 @@
try expect(@TypeOf(y) == f32);}
-
1/1 file10b9130ae9221.test_0...OKAll 1 tests passe
- ed.
+
1/1 filecdc428638149.test_0...OKAll 1 tests passed
+ d.
Another built-in function that is very useful when performing type casting operations is @ptrCast(). In essence, we use the @as() built-in function when we want to explicit convert (or cast) a Zig value/object from a type “x” to a type “y”, etc. However, pointers (we are going to discuss pointers in more depth in Chapter 6) are a special type of object in Zig, i.e. they are treated differently from “normal objects”.
@@ -960,8 +960,8 @@
try expect(@TypeOf(u32_ptr) == *constu32);}
-
1/1 file10b914c434861.test_0...OKAll 1 tests passe
- ed.
+
1/1 filecdc46f71cc03.test_0...OKAll 1 tests passed
+ d.
diff --git a/docs/search.json b/docs/search.json
index 82133e70..9d8728f7 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -124,7 +124,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.\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.\nLow-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.\nIn Zig, we don’t need to use a separate set of tools to build our projects, because a build system is embedded inside the language itself. Therefore, 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 a JSON-like file, in which you can describe your project, and also, declare a set of dependencies of your project that you want to fetch from the internet. In other words, you can use this build.zig.zon file to include a list of external libraries in your project.\nOne possible way to include an external Zig library in your project, is to manually build and install the library in your system, and just link your source code with the library at the build step of your project.\nHowever, if this external Zig library is available on GitHub for example, and it has a valid build.zig.zon file in root folder of the project, which describes the project, you can easily include this library in your project by simply listing this external library in your build.zig.zon file.\nIn other words, this build.zig.zon file works similarly to the package.json file in Javascript projects, or the Pipfile file in Python projects, or the Cargo.toml file in Rust projects. You can read more about this specific file in a couple of articles on the internet23, and you can also see the expected schema for this build.zig.zon file in a documentation file inside the official repository of Zig4.\n\n\n1.2.2 The file root.zig\nLet’s take a look into the root.zig file. You might have noticed that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language5.\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. 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 a strongly-typed language. There are some specific situations where you can (if you want to) omit the type of an object in your code, if this type can be inferred by the zig compiler (we talk more about that at Section 2.4). But there are other situations where you do need to be explicit. For example, you do have to explicitly specify the type of each function argument, and also, the return type of every function that you create in Zig.\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. Therefore, if you are writing a library for other people to use, you have to expose the functions you write in the public API of this library by using this export keyword. 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.\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 there are some other elements that we haven’t 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. The return type of the function (void) is accompanied by an exclamation mark (!). This exclamation mark tells us that this main() function might return an error.\nIs worth noting that, a main() function in Zig is allowed to return nothing (void), or an unsigned 8-bit integer (u8) value6, or an error. In other words, you can write your main() function in Zig to return essentially nothing (void), or, if you prefer, you can also write a more C-like main() function, which returns an integer value that usually serves as a “status code” for the process.\nIn this example, the return type annotation of main() indicates that this function can either return nothing (void), or return an error. This exclamation mark in the return type annotation is an interesting and powerful feature of Zig. In summary, if you write a function and something inside 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 and make it clear that this function might return an error.\nexplicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through a try catch pattern. Zig does have both try and catch keywords. But they work a little differently than what you’re probably used to in other languages.\nIf we look at the main() function below, you can see that we do have a try keyword on the 5th line. But we do not have a catch keyword in this code. In Zig, we use the try keyword to execute an expression that might return an error, which, in this example, is the stdout.print() expression.\nIn essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword does absolutely nothing. It only passes the value forward. It is like if this try keyword was never there. However, if the expression does return an error, then, the try keyword will unwrap the error value, then, it returns this error from the function and also prints the current 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 stop even if you don’t want to stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. It marks the main() function as a public function from this module. Every function in your Zig module is by default private to this Zig module and can only be called from within the module. Unless, you explicitly mark this function as a public function with the pub keyword.\nIf you think about it, this pub keyword in Zig does essentially the opposite of what the static keyword do in C/C++. By making a function “public” you allow other Zig modules to access and call this function. A calling Zig module imports another module by using the @import() built-in function, which makes all public functions from the imported module visible to the calling 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 Important note for Windows users\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.\nAn example of that is accessing the stdout (i.e. the standard output of your system), which is usually done in Zig by using the expression std.io.getStdOut(). Thus, if you use this expression to instantiate a global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows, with a “unable to evaluate comptime expression” error message.\nThis failure in the compilation process happens because all global variables in Zig are initialized at compile-time. However, operations like accessing the stdout (or opening a file) on Windows depends on resources that are available only at runtime (you will learn more about compile-time versus runtime at Section 3.1.1).\nFor example, if you try to compile this code example on Windows, you will likely get the error message exposed below:\n\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\nTo avoid this problem on Windows, we need to force the zig compiler to instantiate this stdout object only at runtime, instead of instantiating it at compile-time. We can achieve that by simply moving the expression to a function body.\nThis solves the problem because all expressions that are inside a function body in Zig are evaluated only at runtime, unless you use the comptime keyword explicitly to change this behaviour. You will learn more about this comptime keyword at Section 12.1.\n\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n\nHello\nYou can read more details about this Windows-specific limitation at a couple of GitHub issues opened at the official Zig repository. More specifically, the issues 17186 7 and 19864 8.\n\n\n1.2.7 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 does 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 init 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.\nLow-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.\nIn Zig, we don’t need to use a separate set of tools to build our projects, because a build system is embedded inside the language itself. Therefore, 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 a JSON-like file, in which you can describe your project, and also, declare a set of dependencies of your project that you want to fetch from the internet. In other words, you can use this build.zig.zon file to include a list of external libraries in your project.\nOne possible way to include an external Zig library in your project, is to manually build and install the library in your system, and just link your source code with the library at the build step of your project.\nHowever, if this external Zig library is available on GitHub for example, and it has a valid build.zig.zon file in root folder of the project, which describes the project, you can easily include this library in your project by simply listing this external library in your build.zig.zon file.\nIn other words, this build.zig.zon file works similarly to the package.json file in Javascript projects, or the Pipfile file in Python projects, or the Cargo.toml file in Rust projects. You can read more about this specific file in a couple of articles on the internet23, and you can also see the expected schema for this build.zig.zon file in a documentation file inside the official repository of Zig4.\n\n\n1.2.2 The file root.zig\nLet’s take a look into the root.zig file. You might have noticed that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language5.\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. 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 a strongly-typed language. There are some specific situations where you can (if you want to) omit the type of an object in your code, if this type can be inferred by the zig compiler (we talk more about that at Section 2.4). But there are other situations where you do need to be explicit. For example, you do have to explicitly specify the type of each function argument, and also, the return type of every function that you create in Zig.\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. Therefore, if you are writing a library for other people to use, you have to expose the functions you write in the public API of this library by using this export keyword. 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.\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 there are some other elements that we haven’t 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. The return type of the function (void) is accompanied by an exclamation mark (!). This exclamation mark tells us that this main() function might return an error.\nIs worth noting that, a main() function in Zig is allowed to return nothing (void), or an unsigned 8-bit integer (u8) value6, or an error. In other words, you can write your main() function in Zig to return essentially nothing (void), or, if you prefer, you can also write a more C-like main() function, which returns an integer value that usually serves as a “status code” for the process.\nIn this example, the return type annotation of main() indicates that this function can either return nothing (void), or return an error. This exclamation mark in the return type annotation is an interesting and powerful feature of Zig. In summary, if you write a function and something inside 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 and make it clear that this function might return an error.\nexplicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through a try catch pattern. Zig does have both try and catch keywords. But they work a little differently than what you’re probably used to in other languages.\nIf we look at the main() function below, you can see that we do have a try keyword on the 5th line. But we do not have a catch keyword in this code. In Zig, we use the try keyword to execute an expression that might return an error, which, in this example, is the stdout.print() expression.\nIn essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword does absolutely nothing. It only passes the value forward. It is like if this try keyword was never there. However, if the expression does return an error, then, the try keyword will unwrap the error value, then, it returns this error from the function and also prints the current 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 stop even if you don’t want to stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. It marks the main() function as a public function from this module. Every function in your Zig module is by default private to this Zig module and can only be called from within the module. Unless, you explicitly mark this function as a public function with the pub keyword.\nIf you think about it, this pub keyword in Zig does essentially the opposite of what the static keyword do in C/C++. By making a function “public” you allow other Zig modules to access and call this function. A calling Zig module imports another module by using the @import() built-in function, which makes all public functions from the imported module visible to the calling 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 Important note for Windows users\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.\nAn example of that is accessing the stdout (i.e. the standard output of your system), which is usually done in Zig by using the expression std.io.getStdOut(). Thus, if you use this expression to instantiate a global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows, with a “unable to evaluate comptime expression” error message.\nThis failure in the compilation process happens because all global variables in Zig are initialized at compile-time. However, operations like accessing the stdout (or opening a file) on Windows depends on resources that are available only at runtime (you will learn more about compile-time versus runtime at Section 3.1.1).\nFor example, if you try to compile this code example on Windows, you will likely get the error message exposed below:\n\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\nTo avoid this problem on Windows, we need to force the zig compiler to instantiate this stdout object only at runtime, instead of instantiating it at compile-time. We can achieve that by simply moving the expression to a function body.\nThis solves the problem because all expressions that are inside a function body in Zig are evaluated only at runtime, unless you use the comptime keyword explicitly to change this behaviour. You will learn more about this comptime keyword at Section 12.1.\n\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n\nHello\nYou can read more details about this Windows-specific limitation at a couple of GitHub issues opened at the official Zig repository. More specifically, the issues 17186 7 and 19864 8.\n\n\n1.2.7 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 does 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": [
"1Introducing Zig"
]
@@ -144,7 +144,7 @@
"href": "Chapters/01-zig-weird.html#sec-assignments",
"title": "1 Introducing Zig",
"section": "1.4 Creating new objects in Zig (i.e. identifiers)",
- "text": "1.4 Creating new objects in Zig (i.e. identifiers)\nLet’s talk more about objects in Zig. Readers that have past experience with other programming languages might know this concept through a different name, such as: “variable” or “identifier”. In this book, I choose to use the term “object” to refer to this concept.\nTo create a new object (or a new “identifier”) in Zig, we use the keywords const or var. These keywords specify if the object that you are creating is mutable or not. If you use const, then the object you are creating is a constant (or immutable) object, which means that once you declare this object, you can no longer change the value stored inside this object.\nOn the other side, if you use var, then, you are creating a variable (or mutable) object. You can change the value of this object as many times you want. Using the keyword var in Zig is similar to using the keywords let mut in Rust.\n\n1.4.1 Constant objects vs variable objects\nIn the code example below, we are creating a new constant object called age. This object stores a number representing the age of someone. However, this code example does not compiles successfully. Because on the next line of code, we are trying to change the value of the object age to 25.\nThe zig compiler detects that we are trying to change the value of an object/identifier that is constant, and because of that, the compiler will raise a compilation error, warning us about the mistake.\n\nconst age = 24;\n// The line below is not valid!\nage = 25;\n\nt.zig:10:5: error: cannot assign to constant\n age = 25;\n ~~^~~\nIn contrast, if you use var, then, the object created is a variable object. With var you can declare this object in your source code, and then, change the value of this object how many times you want over future points in your source code.\nSo, using the same code example exposed above, if I change the declaration of the age object to use the var keyword, then, the program gets compiled successfully. Because now, the zig compiler detects that we are changing the value of an object that allows this behaviour, because it is an “variable object”.\n\nvar age: u8 = 24;\nage = 25;\n\n\n\n1.4.2 Declaring without an initial value\nBy default, when you declare a new object in Zig, you must give it an initial value. In other words, this means that we have to declare, and, at the same time, initialize every object we create in our source code.\nOn the other hand, you can, in fact, declare a new object in your source code, and not give it an explicit value. But we need to use a special keyword for that, which is the undefined keyword.\nIs important to emphasize that, you should avoid using undefined as much as possible. Because when you use this keyword, you leave your object uninitialized, and, as a consequence, if for some reason, your code use this object while it is uninitialized, then, you will definitely have undefined behaviour and major bugs in your program.\nIn the example below, I’m declaring the age object again. But this time, I do not give it an initial value. The variable is only initialized at the second line of code, where I store the number 25 in this object.\n\nvar age: u8 = undefined;\nage = 25;\n\nHaving these points in mind, just remember that you should avoid as much as possible to use undefined in your code. Always declare and initialize your objects. Because this gives you much more safety in your program. But in case you really need to declare an object without initializing it… the undefined keyword is the way to do it in Zig.\n\n\n1.4.3 There is no such thing as unused objects\nEvery object (being constant or variable) that you declare in Zig must be used in some way. You can give this object to a function call, as a function argument, or, you can use it in another expression to calculate the value of another object, or, you can call a method that belongs to this particular object.\nIt doesn’t matter in which way you use it. As long as you use it. If you try to break this rule, i.e. if your try to declare a object, but not use it, the zig compiler will not compile your Zig source code, and it will issue a error message warning that you have unused objects in your code.\nLet’s demonstrate this with an example. In the source code below, we declare a constant object called age. If you try to compile a simple Zig program with this line of code below, the compiler will return an error as demonstrated below:\n\nconst age = 15;\n\nt.zig:4:11: error: unused local constant\n const age = 15;\n ^~~\nEverytime you declare a new object in Zig, you have two choices:\n\nyou either use the value of this object;\nor 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 this object to an special character in Zig, which is the underscore (_). When you assign an object to a underscore, like in the example below, the zig compiler will automatically discard the value of this particular object.\nYou can see in the example below that, this time, the compiler did not complain about any “unused constant”, and successfully compiled our source code.\n\n// It compiles!\nconst age = 15;\n_ = age;\n\nNow, remember, everytime you assign a particular object to the underscore, this object is essentially destroyed. It is discarded by the compiler. This means that you can no longer use this object further in your code. It doesn’t exist anymore.\nSo if you try to use the constant age in the example below, after we discarded it, you will get a loud error message from the compiler (talking about a “pointless discard”) warning you about this mistake.\n\n// It does not compile.\nconst age = 15;\n_ = age;\n// Using a discarded value!\nstd.debug.print(\"{d}\\n\", .{age + 2});\n\nt.zig:7:5: error: pointless discard\n of local constant\nThis same rule applies to variable objects. Every variable object must also be used in some way. And if you assign a variable object to the underscore, this object also get’s discarded, and you can no longer use this object.\n\n\n1.4.4 You must mutate every variable objects\nEvery variable object that you create in your source code must be mutated at some point. In other words, if you declare an object as a variable object, with the keyword var, and you do not change the value of this object at some point in the future, the zig compiler will detect this, and it will raise an error warning you about this mistake.\nThe concept behind this is that every object you create in Zig should be preferably a constant object, unless you really need an object whose value will change during the execution of your program.\nSo, if I try to declare a variable object such as where_i_live below, and I do not change the value of this object in some way, the zig compiler raises an error message with the phrase “variable is never mutated”.\n\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n\nt.zig:7:5: error: local variable is never mutated\nt.zig:7:5: note: consider using 'const'",
+ "text": "1.4 Creating new objects in Zig (i.e. identifiers)\nLet’s talk more about objects in Zig. Readers that have past experience with other programming languages might know this concept through a different name, such as: “variable” or “identifier”. In this book, I choose to use the term “object” to refer to this concept.\nTo create a new object (or a new “identifier”) in Zig, we use the keywords const or var. These keywords specify if the object that you are creating is mutable or not. If you use const, then the object you are creating is a constant (or immutable) object, which means that once you declare this object, you can no longer change the value stored inside this object.\nOn the other side, if you use var, then, you are creating a variable (or mutable) object. You can change the value of this object as many times you want. Using the keyword var in Zig is similar to using the keywords let mut in Rust.\n\n1.4.1 Constant objects vs variable objects\nIn the code example below, we are creating a new constant object called age. This object stores a number representing the age of someone. However, this code example does not compile successfully. Because on the next line of code, we are trying to change the value of the object age to 25.\nThe zig compiler detects that we are trying to change the value of an object/identifier that is constant, and because of that, the compiler will raise a compilation error, warning us about the mistake.\n\nconst age = 24;\n// The line below is not valid!\nage = 25;\n\nt.zig:10:5: error: cannot assign to constant\n age = 25;\n ~~^~~\nIn contrast, if you use var, then, the object created is a variable object. With var you can declare this object in your source code, and then, change the value of this object how many times you want over future points in your source code.\nSo, using the same code example exposed above, if I change the declaration of the age object to use the var keyword, then, the program gets compiled successfully. Because now, the zig compiler detects that we are changing the value of an object that allows this behaviour, because it is an “variable object”.\n\nvar age: u8 = 24;\nage = 25;\n\n\n\n1.4.2 Declaring without an initial value\nBy default, when you declare a new object in Zig, you must give it an initial value. In other words, this means that we have to declare, and, at the same time, initialize every object we create in our source code.\nOn the other hand, you can, in fact, declare a new object in your source code, and not give it an explicit value. But we need to use a special keyword for that, which is the undefined keyword.\nIs important to emphasize that, you should avoid using undefined as much as possible. Because when you use this keyword, you leave your object uninitialized, and, as a consequence, if for some reason, your code use this object while it is uninitialized, then, you will definitely have undefined behaviour and major bugs in your program.\nIn the example below, I’m declaring the age object again. But this time, I do not give it an initial value. The variable is only initialized at the second line of code, where I store the number 25 in this object.\n\nvar age: u8 = undefined;\nage = 25;\n\nHaving these points in mind, just remember that you should avoid as much as possible to use undefined in your code. Always declare and initialize your objects. Because this gives you much more safety in your program. But in case you really need to declare an object without initializing it… the undefined keyword is the way to do it in Zig.\n\n\n1.4.3 There is no such thing as unused objects\nEvery object (being constant or variable) that you declare in Zig must be used in some way. You can give this object to a function call, as a function argument, or, you can use it in another expression to calculate the value of another object, or, you can call a method that belongs to this particular object.\nIt doesn’t matter in which way you use it. As long as you use it. If you try to break this rule, i.e. if your try to declare a object, but not use it, the zig compiler will not compile your Zig source code, and it will issue a error message warning that you have unused objects in your code.\nLet’s demonstrate this with an example. In the source code below, we declare a constant object called age. If you try to compile a simple Zig program with this line of code below, the compiler will return an error as demonstrated below:\n\nconst age = 15;\n\nt.zig:4:11: error: unused local constant\n const age = 15;\n ^~~\nEverytime you declare a new object in Zig, you have two choices:\n\nyou either use the value of this object;\nor 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 this object to an special character in Zig, which is the underscore (_). When you assign an object to a underscore, like in the example below, the zig compiler will automatically discard the value of this particular object.\nYou can see in the example below that, this time, the compiler did not complain about any “unused constant”, and successfully compiled our source code.\n\n// It compiles!\nconst age = 15;\n_ = age;\n\nNow, remember, everytime you assign a particular object to the underscore, this object is essentially destroyed. It is discarded by the compiler. This means that you can no longer use this object further in your code. It doesn’t exist anymore.\nSo if you try to use the constant age in the example below, after we discarded it, you will get a loud error message from the compiler (talking about a “pointless discard”) warning you about this mistake.\n\n// It does not compile.\nconst age = 15;\n_ = age;\n// Using a discarded value!\nstd.debug.print(\"{d}\\n\", .{age + 2});\n\nt.zig:7:5: error: pointless discard\n of local constant\nThis same rule applies to variable objects. Every variable object must also be used in some way. And if you assign a variable object to the underscore, this object also get’s discarded, and you can no longer use this object.\n\n\n1.4.4 You must mutate every variable objects\nEvery variable object that you create in your source code must be mutated at some point. In other words, if you declare an object as a variable object, with the keyword var, and you do not change the value of this object at some point in the future, the zig compiler will detect this, and it will raise an error warning you about this mistake.\nThe concept behind this is that every object you create in Zig should be preferably a constant object, unless you really need an object whose value will change during the execution of your program.\nSo, if I try to declare a variable object such as where_i_live below, and I do not change the value of this object in some way, the zig compiler raises an error message with the phrase “variable is never mutated”.\n\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n\nt.zig:7:5: error: local variable is never mutated\nt.zig:7:5: note: consider using 'const'",
"crumbs": [
"1Introducing Zig"
]
@@ -184,7 +184,7 @@
"href": "Chapters/01-zig-weird.html#sec-zig-strings",
"title": "1 Introducing Zig",
"section": "1.8 How strings work in Zig?",
- "text": "1.8 How strings work in Zig?\nThe first project that we are going to build and discuss in this book is a base64 encoder/decoder (Chapter 4). But in order for us to build such a thing, we need to get a better understanding on how strings work in Zig. So let’s discuss this specific aspect of Zig.\nIn summary, there are two types of string values that you care about in Zig, which are:\n\nString literal values.\nString objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string). But in Zig, a string literal value also embeds the length of the string into the data type of the value itself. Therefore, a string literal value have a data type in the format *const [n:0]u8. The n in the data type indicates the size of the string.\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes, or, in other words, a slice of u8 values (slices were presented at Section 1.6). Thus, a string object have a data type of []u8 or []const u8, depending if the string object is marked as constant with const, or as variable with var.\nBecause a string object is essentially a slice, it means that a string object always contains two things: a pointer to an array of bytes (i.e. u8 values) that represents the string value; and also, a length value, which specifies the size of the slice, or, how many elements there is in the slice. Is worth to emphasize that the array of bytes in a string object is not null-terminated, like in a string literal value.\n\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\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every sequence of bytes you’re working with, but is not really Zig’s job to fix the encoding of your strings (you can use iconv22 for that). Today, most of the text in our modern world, especially on the web, should be UTF-8 encoded. So if your string literal is not UTF-8 encoded, then, you will likely have problems in Zig.\nLet’s take for example the word “Hello”. In UTF-8, this sequence of characters (H, e, l, l, o) is represented by the sequence of decimal numbers 72, 101, 108, 108, 111. In hexadecimal, this sequence is 0x48, 0x65, 0x6C, 0x6C, 0x6F. So if I take this sequence of hexadecimal values, and ask Zig to print this sequence of bytes as a sequence of characters (i.e. a string), then, the text “Hello” will be printed into the terminal:\n\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\nHello\n\n\nIf you want to see the actual bytes that represents a string in Zig, you can use a for loop to iterate through each byte in the string, and ask Zig to print each byte as an hexadecimal value to the terminal. You do that by using a print() statement with the X formatting specifier, like you would normally do with the printf() function23 in C.\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example\";\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\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 \n\n\n\n1.8.1 Strings in C\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well. In more details, string values in C are treated internally as an array of arbitrary bytes, and this array is also null-terminated.\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of the string inside the object itself. In the case of a string literal value, this length is stored in the data type of the value (i.e. the n variable in [n:0]u8). While, in a string object, the length is stored in the len attribute of the slice that represents the string object. This small detail makes your code safer, because is much easier for the Zig compiler to check if you are trying to access an element that is “out of bounds”, i.e. if your trying to access memory that does not belong to you.\nTo achieve this same kind of safety in C, you have to do a lot of work that kind of seems pointless. So getting this kind of safety is not automatic and much harder to do in C. For example, if you want to track the length of your string throughout your program in C, then, you first need to loop through the array of bytes that represents this string, and find the null element ('\\0') position to discover where exactly the array ends, or, in other words, to find how much elements the array of bytes contain.\nTo do that, you would need something like this in C. In this example, the C string stored in the object array is 25 bytes long:\n\n#include <stdio.h>\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\nNumber of elements in the array: 25\nYou don’t have this kind of work in Zig. Because the length of the string is always present and accessible. In a string object for example, you can easily access the length of the string through the len attribute. As an example, the string_object object below is 43 bytes long:\n\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\n43\n\n\n\n\n1.8.2 A better look at the object type\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 @TypeOf() function. If we look at the type of the simple_array object below, you will find that this object is an array of 4 elements. Each element is a signed integer of 32 bits which corresponds to the data type i32 in Zig. That is what an object of type [4]i32 is.\nBut if we look closely at the type of the string literal value exposed below, you will find that this object is a constant pointer (hence the *const annotation) to an array of 16 elements (or 16 bytes). Each element is a single byte (more precisely, an unsigned 8 bit integer - u8), that is why we have the [16:0]u8 portion of the type below. In other words, the string literal value exposed below is 16 bytes long.\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), which is very similar to the type of the string literal value. This demonstrates that a string literal value in Zig is already a pointer to a null-terminated array of bytes.\nFurthermore, if we take a look at the type of the string_obj object, you will see that it is a slice object (hence the [] portion of the type) to a sequence of constant u8 values (hence the const u8 portion of the type).\n\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n\n\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n\n\n1.8.3 Byte vs unicode points\nIs important to point out that each byte in the array is not necessarily a single character. This fact arises from the difference between a single byte and a single unicode point.\nThe encoding UTF-8 works by assigning a number (which is called a unicode point) to each character in the string. For example, the character “H” is stored in UTF-8 as the decimal number 72. This means that the number 72 is the unicode point for the character “H”. Each possible character that can appear in a UTF-8 encoded string have its own unicode point.\nFor example, the Latin Capital Letter A With Stroke (Ⱥ) is represented by the number (or the unicode point) 570. However, this decimal number (570) is higher than the maximum number stored inside a single byte, which is 255. In other words, the maximum decimal number that can be represented with a single byte is 255. That is why, the unicode point 570 is actually stored inside the computer’s memory as the bytes C8 BA.\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"Ⱥ\";\n _ = try stdout.write(\n \"Bytes that represents the string object: \"\n );\n for (string_object) |char| {\n try stdout.print(\"{X} \", .{char});\n }\n}\n\nBytes that represents the string object: C8 BA \n\n\nThis means that to store the character Ⱥ in an UTF-8 encoded string, we need to use two bytes together to represent the number 570. That is why the relationship between bytes and unicode points is not always 1 to 1. Each unicode point is a single character in the string, but not always a single byte corresponds to a single unicode point.\nAll of this means that if you loop trough the elements of a string in Zig, you will be looping through the bytes that represents that string, and not through the characters of that string. In the Ⱥ example above, the for loop needed two iterations (instead of a single iteration) to print the two bytes that represents this Ⱥ letter.\nNow, all english letters (or ASCII letters if you prefer) can be represented by a single byte in UTF-8. As a consequence, if your UTF-8 string contains only english letters (or ASCII letters), then, you are lucky. Because the number of bytes will be equal to the number of characters in that string. In other words, in this specific situation, the relationship between bytes and unicode points is 1 to 1.\nBut on the other side, if your string contains other types of letters… for example, you might be working with text data that contains, chinese, japanese or latin letters, then, the number of bytes necessary to represent your UTF-8 string will likely be much higher than the number of characters in that string.\nIf you need to iterate through the characters of a string, instead of its bytes, then, you can use the std.unicode.Utf8View struct to create an iterator that iterates through the unicode points of your string.\nIn the example below, we loop through the japanese characters “アメリカ”. Each of the four characters in this string is represented by three bytes. But the for loop iterates four times, one iteration for each character/unicode point in this string:\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.nextCodepointSlice()) |codepoint| {\n try stdout.print(\n \"got codepoint {}\\n\",\n .{std.fmt.fmtSliceHexUpper(codepoint)},\n );\n }\n}\n\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n\n\n1.8.4 Some useful functions for strings\nIn this section, I just want to quickly describe some functions from the Zig Standard Library that are very useful to use when working with strings. Most notably:\n\nstd.mem.eql(): to compare if two strings are equal.\nstd.mem.splitScalar(): to split a string into an array of substrings given a delimiter value.\nstd.mem.splitSequence(): to split a string into an array of substrings given a substring delimiter.\nstd.mem.startsWith(): to check if string starts with substring.\nstd.mem.endsWith(): to check if string ends with substring.\nstd.mem.trim(): to remove specific values from both start and end of the string.\nstd.mem.concat(): to concatenate strings together.\nstd.mem.count(): to count the occurrences of substring in the string.\nstd.mem.replace(): to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the mem module of the Zig Standard Library. This module contains multiple functions and methods that are useful to work with memory and sequences of bytes in general.\nThe eql() function is used to check if two arrays of data are equal or not. Since strings are just arbitrary arrays of bytes, we can use this function to compare two strings together. This function returns a boolean value indicating if the two strings are equal or not. The first argument of this function is the data type of the elements of the arrays that are being compared.\n\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n\ntrue\n\n\nThe splitScalar() and splitSequence() functions are useful to split a string into multiple fragments, like the split() method from Python strings. The difference between these two methods is that the splitScalar() uses a single character as the separator to split the string, while splitSequence() uses a sequence of characters (a.k.a. a substring) as the separator. There is a practical example of these functions later in the book.\nThe startsWith() and endsWith() functions are pretty straightforward. They return a boolean value indicating if the string (or, more precisely, if the array of data) begins (startsWith) or ends (endsWith) with the sequence provided.\n\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n\ntrue\n\n\nThe concat() function, as the name suggests, concatenate two or more strings together. Because the process of concatenating the strings involves allocating enough space to accomodate all the strings together, this concat() function receives an allocator object as input.\n\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n\nAs you can imagine, the replace() function is used to replace substrings in a string by another substring. This function works very similarly to the replace() method from Python strings. Therefore, you provide a substring to search, and every time that the replace() function finds this substring within the input string, it replaces this substring with the “replacement substring” that you provided as input.\nIn the example below, we are taking the input string “Hello”, and replacing all occurrences of the substring “el” inside this input string with “34”, and saving the results inside the buffer object. As result, the replace() function returns an usize value that indicates how many replacements were performed.\n\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n\nNew string: H34lo\nN of replacements: 1",
+ "text": "1.8 How strings work in Zig?\nThe first project that we are going to build and discuss in this book is a base64 encoder/decoder (Chapter 4). But in order for us to build such a thing, we need to get a better understanding on how strings work in Zig. So let’s discuss this specific aspect of Zig.\nIn summary, there are two types of string values that you care about in Zig, which are:\n\nString literal values.\nString objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string). But in Zig, a string literal value also embeds the length of the string into the data type of the value itself. Therefore, a string literal value have a data type in the format *const [n:0]u8. The n in the data type indicates the size of the string.\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes, or, in other words, a slice of u8 values (slices were presented at Section 1.6). Thus, a string object have a data type of []u8 or []const u8, depending if the string object is marked as constant with const, or as variable with var.\nBecause a string object is essentially a slice, it means that a string object always contains two things: a pointer to an array of bytes (i.e. u8 values) that represents the string value; and also, a length value, which specifies the size of the slice, or, how many elements there is in the slice. Is worth to emphasize that the array of bytes in a string object is not null-terminated, like in a string literal value.\n\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\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every sequence of bytes you’re working with, but is not really Zig’s job to fix the encoding of your strings (you can use iconv22 for that). Today, most of the text in our modern world, especially on the web, should be UTF-8 encoded. So if your string literal is not UTF-8 encoded, then, you will likely have problems in Zig.\nLet’s take for example the word “Hello”. In UTF-8, this sequence of characters (H, e, l, l, o) is represented by the sequence of decimal numbers 72, 101, 108, 108, 111. In hexadecimal, this sequence is 0x48, 0x65, 0x6C, 0x6C, 0x6F. So if I take this sequence of hexadecimal values, and ask Zig to print this sequence of bytes as a sequence of characters (i.e. a string), then, the text “Hello” will be printed into the terminal:\n\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\nHello\n\n\nIf you want to see the actual bytes that represents a string in Zig, you can use a for loop to iterate through each byte in the string, and ask Zig to print each byte as an hexadecimal value to the terminal. You do that by using a print() statement with the X formatting specifier, like you would normally do with the printf() function23 in C.\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"This is an example\";\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\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 \n\n\n\n1.8.1 Strings in C\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well. In more details, string values in C are treated internally as an array of arbitrary bytes, and this array is also null-terminated.\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of the string inside the object itself. In the case of a string literal value, this length is stored in the data type of the value (i.e. the n variable in [n:0]u8). While, in a string object, the length is stored in the len attribute of the slice that represents the string object. This small detail makes your code safer, because it is much easier for the Zig compiler to check if you are trying to access an element that is “out of bounds”, i.e. if your trying to access memory that does not belong to you.\nTo achieve this same kind of safety in C, you have to do a lot of work that kind of seems pointless. So getting this kind of safety is not automatic and much harder to do in C. For example, if you want to track the length of your string throughout your program in C, then, you first need to loop through the array of bytes that represents this string, and find the null element ('\\0') position to discover where exactly the array ends, or, in other words, to find how much elements the array of bytes contain.\nTo do that, you would need something like this in C. In this example, the C string stored in the object array is 25 bytes long:\n\n#include <stdio.h>\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\nNumber of elements in the array: 25\nYou don’t have this kind of work in Zig. Because the length of the string is always present and accessible. In a string object for example, you can easily access the length of the string through the len attribute. As an example, the string_object object below is 43 bytes long:\n\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\n43\n\n\n\n\n1.8.2 A better look at the object type\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 @TypeOf() function. If we look at the type of the simple_array object below, you will find that this object is an array of 4 elements. Each element is a signed integer of 32 bits which corresponds to the data type i32 in Zig. That is what an object of type [4]i32 is.\nBut if we look closely at the type of the string literal value exposed below, you will find that this object is a constant pointer (hence the *const annotation) to an array of 16 elements (or 16 bytes). Each element is a single byte (more precisely, an unsigned 8 bit integer - u8), that is why we have the [16:0]u8 portion of the type below. In other words, the string literal value exposed below is 16 bytes long.\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), which is very similar to the type of the string literal value. This demonstrates that a string literal value in Zig is already a pointer to a null-terminated array of bytes.\nFurthermore, if we take a look at the type of the string_obj object, you will see that it is a slice object (hence the [] portion of the type) to a sequence of constant u8 values (hence the const u8 portion of the type).\n\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n\n\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n\n\n1.8.3 Byte vs unicode points\nIs important to point out that each byte in the array is not necessarily a single character. This fact arises from the difference between a single byte and a single unicode point.\nThe encoding UTF-8 works by assigning a number (which is called a unicode point) to each character in the string. For example, the character “H” is stored in UTF-8 as the decimal number 72. This means that the number 72 is the unicode point for the character “H”. Each possible character that can appear in a UTF-8 encoded string have its own unicode point.\nFor example, the Latin Capital Letter A With Stroke (Ⱥ) is represented by the number (or the unicode point) 570. However, this decimal number (570) is higher than the maximum number stored inside a single byte, which is 255. In other words, the maximum decimal number that can be represented with a single byte is 255. That is why, the unicode point 570 is actually stored inside the computer’s memory as the bytes C8 BA.\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\npub fn main() !void {\n const string_object = \"Ⱥ\";\n _ = try stdout.write(\n \"Bytes that represents the string object: \"\n );\n for (string_object) |char| {\n try stdout.print(\"{X} \", .{char});\n }\n}\n\nBytes that represents the string object: C8 BA \n\n\nThis means that to store the character Ⱥ in an UTF-8 encoded string, we need to use two bytes together to represent the number 570. That is why the relationship between bytes and unicode points is not always 1 to 1. Each unicode point is a single character in the string, but not always a single byte corresponds to a single unicode point.\nAll of this means that if you loop trough the elements of a string in Zig, you will be looping through the bytes that represents that string, and not through the characters of that string. In the Ⱥ example above, the for loop needed two iterations (instead of a single iteration) to print the two bytes that represents this Ⱥ letter.\nNow, all english letters (or ASCII letters if you prefer) can be represented by a single byte in UTF-8. As a consequence, if your UTF-8 string contains only english letters (or ASCII letters), then, you are lucky. Because the number of bytes will be equal to the number of characters in that string. In other words, in this specific situation, the relationship between bytes and unicode points is 1 to 1.\nBut on the other side, if your string contains other types of letters… for example, you might be working with text data that contains, chinese, japanese or latin letters, then, the number of bytes necessary to represent your UTF-8 string will likely be much higher than the number of characters in that string.\nIf you need to iterate through the characters of a string, instead of its bytes, then, you can use the std.unicode.Utf8View struct to create an iterator that iterates through the unicode points of your string.\nIn the example below, we loop through the japanese characters “アメリカ”. Each of the four characters in this string is represented by three bytes. But the for loop iterates four times, one iteration for each character/unicode point in this string:\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.nextCodepointSlice()) |codepoint| {\n try stdout.print(\n \"got codepoint {}\\n\",\n .{std.fmt.fmtSliceHexUpper(codepoint)},\n );\n }\n}\n\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n\n\n1.8.4 Some useful functions for strings\nIn this section, I just want to quickly describe some functions from the Zig Standard Library that are very useful to use when working with strings. Most notably:\n\nstd.mem.eql(): to compare if two strings are equal.\nstd.mem.splitScalar(): to split a string into an array of substrings given a delimiter value.\nstd.mem.splitSequence(): to split a string into an array of substrings given a substring delimiter.\nstd.mem.startsWith(): to check if string starts with substring.\nstd.mem.endsWith(): to check if string ends with substring.\nstd.mem.trim(): to remove specific values from both start and end of the string.\nstd.mem.concat(): to concatenate strings together.\nstd.mem.count(): to count the occurrences of substring in the string.\nstd.mem.replace(): to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the mem module of the Zig Standard Library. This module contains multiple functions and methods that are useful to work with memory and sequences of bytes in general.\nThe eql() function is used to check if two arrays of data are equal or not. Since strings are just arbitrary arrays of bytes, we can use this function to compare two strings together. This function returns a boolean value indicating if the two strings are equal or not. The first argument of this function is the data type of the elements of the arrays that are being compared.\n\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n\ntrue\n\n\nThe splitScalar() and splitSequence() functions are useful to split a string into multiple fragments, like the split() method from Python strings. The difference between these two methods is that the splitScalar() uses a single character as the separator to split the string, while splitSequence() uses a sequence of characters (a.k.a. a substring) as the separator. There is a practical example of these functions later in the book.\nThe startsWith() and endsWith() functions are pretty straightforward. They return a boolean value indicating if the string (or, more precisely, if the array of data) begins (startsWith) or ends (endsWith) with the sequence provided.\n\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n\ntrue\n\n\nThe concat() function, as the name suggests, concatenate two or more strings together. Because the process of concatenating the strings involves allocating enough space to accomodate all the strings together, this concat() function receives an allocator object as input.\n\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n\nAs you can imagine, the replace() function is used to replace substrings in a string by another substring. This function works very similarly to the replace() method from Python strings. Therefore, you provide a substring to search, and every time that the replace() function finds this substring within the input string, it replaces this substring with the “replacement substring” that you provided as input.\nIn the example below, we are taking the input string “Hello”, and replacing all occurrences of the substring “el” inside this input string with “34”, and saving the results inside the buffer object. As result, the replace() function returns an usize value that indicates how many replacements were performed.\n\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n\nNew string: H34lo\nN of replacements: 1",
"crumbs": [
"1Introducing Zig"
]
@@ -194,7 +194,7 @@
"href": "Chapters/01-zig-weird.html#safety-in-zig",
"title": "1 Introducing Zig",
"section": "1.9 Safety in Zig",
- "text": "1.9 Safety in Zig\nA general trend in modern low-level programming languages is safety. As our modern world becomes more interconnected with technology and computers, the data produced by all of this technology becomes one of the most important (and also, one of the most dangerous) assets that we have.\nThis is probably the main reason why modern low-level programming languages have been giving great attention to safety, especially memory safety, because memory corruption is still the main target for hackers to exploit. The reality is that we don’t have an easy solution for this problem. For now, we only have techniques and strategies that mitigates these problems.\nAs Richard Feldman explains on his most recent GOTO conference talk24 , we haven’t figured it out yet a way to achieve true safety in technology. In other words, we haven’t found a way to build software that won’t be exploited with 100% certainty. We can greatly reduce the risks of our software being exploited, by ensuring memory safety for example. But this is not enough to achieve “true safety” territory.\nBecause even if you write your program in a “safe language”, hackers can still exploit failures in the operating system where your program is running (e.g. maybe the system where your code is running has a “backdoor exploit” that can still affect your code in unexpected ways), or also, they can exploit the features from the architecture of your computer. A recently found exploit that involves memory invalidation through a feature of “memory tags” present in ARM chips is an example of that (Kim et al. 2024).\nThe question is: what have Zig and other languages been doing to mitigate this problem? If we take Rust as an example, Rust is, for the most part25, a memory safe language by enforcing specific rules to the developer. In other words, the key feature of Rust, the borrow checker, forces you to follow a specific logic when you are writing your Rust code, and the Rust compiler will always complain everytime you try to go out of this pattern.\nIn contrast, the Zig language is not a memory safe language by default. There are some memory safety features that you get for free in Zig, especially in arrays and pointer objects. But there are other tools offered by the language, that are not used by default. In other words, the zig compiler does not obligates you to use such tools.\nThe tools listed below are related to memory safety. That is, they help you to achieve memory safety in your Zig code:\n\ndefer allows you to keep free operations physically 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.\nerrdefer helps you to guarantee that your program frees the allocated memory, even if a runtime error occurs.\npointers and objects are non-nullable by default. This helps you to avoid memory problems that might arise from de-referencing null pointers.\nZig 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 transform your unit tests into a weapon that you can use to detect memory problems in your code.\narrays 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\nDespite these features that Zig offers that are related to memory safety issues, the language also has some rules that help you to achieve another type of safety, which is more related to program logic safety. These rules are:\n\npointers and objects are non-nullable by default. Which eliminates an edge case that might break the logic of your program.\nswitch statements must exaust all possible options.\nthe zig compiler forces you to handle every possible error in your program.",
+ "text": "1.9 Safety in Zig\nA general trend in modern low-level programming languages is safety. As our modern world becomes more interconnected with technology and computers, the data produced by all of this technology becomes one of the most important (and also, one of the most dangerous) assets that we have.\nThis is probably the main reason why modern low-level programming languages have been giving great attention to safety, especially memory safety, because memory corruption is still the main target for hackers to exploit. The reality is that we don’t have an easy solution for this problem. For now, we only have techniques and strategies that mitigates these problems.\nAs Richard Feldman explains on his most recent GOTO conference talk24 , we haven’t figured it out yet a way to achieve true safety in technology. In other words, we haven’t found a way to build software that won’t be exploited with 100% certainty. We can greatly reduce the risks of our software being exploited, by ensuring memory safety for example. But this is not enough to achieve “true safety” territory.\nBecause even if you write your program in a “safe language”, hackers can still exploit failures in the operating system where your program is running (e.g. maybe the system where your code is running has a “backdoor exploit” that can still affect your code in unexpected ways), or also, they can exploit the features from the architecture of your computer. A recently found exploit that involves memory invalidation through a feature of “memory tags” present in ARM chips is an example of that (Kim et al. 2024).\nThe question is: what have Zig and other languages been doing to mitigate this problem? If we take Rust as an example, Rust is, for the most part25, a memory safe language by enforcing specific rules to the developer. In other words, the key feature of Rust, the borrow checker, forces you to follow a specific logic when you are writing your Rust code, and the Rust compiler will always complain everytime you try to go out of this pattern.\nIn contrast, the Zig language is not a memory safe language by default. There are some memory safety features that you get for free in Zig, especially in arrays and pointer objects. But there are other tools offered by the language, that are not used by default. In other words, the zig compiler does not obligate you to use such tools.\nThe tools listed below are related to memory safety. That is, they help you to achieve memory safety in your Zig code:\n\ndefer allows you to keep free operations physically 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.\nerrdefer helps you to guarantee that your program frees the allocated memory, even if a runtime error occurs.\npointers and objects are non-nullable by default. This helps you to avoid memory problems that might arise from de-referencing null pointers.\nZig 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 transform your unit tests into a weapon that you can use to detect memory problems in your code.\narrays 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\nDespite these features that Zig offers that are related to memory safety issues, the language also has some rules that help you to achieve another type of safety, which is more related to program logic safety. These rules are:\n\npointers and objects are non-nullable by default. Which eliminates an edge case that might break the logic of your program.\nswitch statements must exaust all possible options.\nthe zig compiler forces you to handle every possible error in your program.",
"crumbs": [
"1Introducing Zig"
]
@@ -234,7 +234,7 @@
"href": "Chapters/03-structs.html#sec-zig-control-flow",
"title": "2 Control flow, structs, modules and types",
"section": "",
- "text": "2.1.1 If/else statements\nAn if/else statement performs a “conditional flow operation”. A conditional flow control (or choice control) allows you to execute or ignore a certain block of commands based on a logical condition. Many programmers and computer science professionals also use the term “branching” in this case. In essence, an if/else statement allow us to use the result of a logical test to decide whether or not to execute a given block of commands.\nIn Zig, we write if/else statements by using the keywords if and else. We start with the if keyword followed by a logical test inside a pair of parentheses, followed by a pair of curly braces which contains the lines of code to be executed in case the logical test returns the value true.\nAfter that, you can optionally add an else statement. To do that, just add the else keyword followed by a pair of curly braces, with the lines of code to executed in case the logical test defined at if returns false.\nIn the example below, we are testing if the object x contains a number that is greater than 10. Judging by the output printed to the console, we know that this logical test returned false. Because the output in the console is compatible with the line of code present in the else branch of the if/else statement.\n\nconst x = 5;\nif (x > 10) {\n try stdout.print(\n \"x > 10!\\n\", .{}\n );\n} else {\n try stdout.print(\n \"x <= 10!\\n\", .{}\n );\n}\n\nx <= 10!\n\n\n\n\n2.1.2 Switch statements\nSwitch statements are also available in Zig, and they have a very similar syntax to a switch statement in Rust. As you would expect, to write a switch statement in Zig we use the switch keyword. We provide the value that we want to “switch over” inside a pair of parentheses. Then, we list the possible combinations (or “branches”) inside a pair of curly braces.\nLet’s take a look at the code example below. You can see that I’m creating an enum type called Role. We talk more about enums in Section 7.6. But in summary, this Role type is listing different types of roles in a fictitious company, like SE for Software Engineer, DE for Data Engineer, PM for Product Manager, etc.\nNotice that we are using the value from the role object in the switch statement, to discover which exact area we need to store in the area variable object. Also notice that we are using type inference inside the switch statement, with the dot character, as we are going to describe in Section 2.4. This makes the zig compiler infer the correct data type of the values (PM, SE, etc.) for us.\nAlso notice that, we are grouping multiple values in the same branch of the switch statement. We just separate each possible value with a comma. For example, if role contains either DE or DA, the area variable would contain the value \"Data & Analytics\", instead of \"Platform\" or \"Sales\".\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst Role = enum {\n SE, DPE, DE, DA, PM, PO, KS\n};\n\npub fn main() !void {\n var area: []const u8 = undefined;\n const role = Role.SE;\n switch (role) {\n .PM, .SE, .DPE, .PO => {\n area = \"Platform\";\n },\n .DE, .DA => {\n area = \"Data & Analytics\";\n },\n .KS => {\n area = \"Sales\";\n },\n }\n try stdout.print(\"{s}\\n\", .{area});\n}\n\nPlatform\n\n\n\n2.1.2.1 Switch statements must exhaust all possibilities\nOne very important aspect about switch statements in Zig is that they must exhaust all existing possibilities. In other words, all possible values that could be found inside the order object must be explicitly handled in this switch statement.\nSince the role object have type Role, the only possible values to be found inside this object are PM, SE, DPE, PO, DE, DA and KS. There are no other possible values to be stored in this role object. Thus, the switch statements must have a combination (branch) for each one of these values. This is what “exhaust all existing possibilities” means. The switch statement covers every possible case.\nTherefore, you cannot write a switch statement in Zig, and leave an edge case with no explicit action to be taken. This is a similar behaviour to switch statements in Rust, which also have to handle all possible cases.\n\n\n2.1.2.2 The else branch\nTake a look at the dump_hex_fallible() function below as an example. This function comes from the Zig Standard Library. More precisely, from the debug.zig module1. There are multiple lines in this function, but I omitted them to focus solely on the switch statement found in this function. Notice that this switch statement has four possible cases (i.e. four explicit branches). Also, notice that we used an else branch in this case.\nAn else branch in a switch statement works as the “default branch”. Whenever you have multiple cases in your switch statement where you want to apply the exact same action, you can use an else branch to do that.\n\npub fn dump_hex_fallible(bytes: []const u8) !void {\n // Many lines ...\n switch (byte) {\n '\\n' => try writer.writeAll(\"␊\"),\n '\\r' => try writer.writeAll(\"␍\"),\n '\\t' => try writer.writeAll(\"␉\"),\n else => try writer.writeByte('.'),\n }\n}\n\nMany programmers would also use an else branch to handle a “not supported” case. That is, a case that cannot be properly handled by your code, or, just a case that should not be “fixed”. Therefore, you can use an else branch to panic (or raise an error) in your program to stop the current execution.\nTake the code example below. We can see that, we are handling the cases for the level object being either 1, 2, or 3. All other possible cases are not supported by default, and, as consequence, we raise a runtime error in such cases through the @panic() built-in function.\nAlso notice that, we are assigning the result of the switch statement to a new object called category. This is another thing that you can do with switch statements in Zig. If a branch outputs a value as result, you can store the result value of the switch statement into a new object.\n\nconst level: u8 = 4;\nconst category = switch (level) {\n 1, 2 => \"beginner\",\n 3 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n\nthread 13103 panic: Not supported level!\nt.zig:9:13: 0x1033c58 in main (switch2)\n @panic(\"Not supported level!\");\n ^\n\n\n2.1.2.3 Using ranges in switch\nFurthermore, you can also use ranges of values in switch statements. That is, you can create a branch in your switch statement that is used whenever the input value is within the specified range. These “range expressions” are created with the operator .... It is important to emphasize that the ranges created by this operator are inclusive on both ends.\nFor example, I could easily change the previous code example to support all levels between 0 and 100. Like this:\n\nconst level: u8 = 4;\nconst category = switch (level) {\n 0...25 => \"beginner\",\n 26...75 => \"intermediary\",\n 76...100 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n\nbeginner\n\n\nThis is neat, and it works with character ranges too. That is, I could simply write 'a'...'z', to match any character value that is a lowercase letter, and it would work fine.\n\n\n2.1.2.4 Labeled switch statements\nIn Section 1.7 we have talked about labeling blocks, and also, about using these labels to return a value from the block. Well, from version 0.14.0 and onwards of the zig compiler, you can also apply labels over switch statements, which makes it possible to almost implement a “C goto” like pattern.\nFor example, if you give the label xsw to a switch statement, you can use this label in conjunction with the continue keyword to go back to the beginning of the switch statement. In the example below, the execution goes back to the beginning of the switch statement two times, before ending at the 3 branch.\n\nxsw: switch (@as(u8, 1)) {\n 1 => {\n try stdout.print(\"First branch\\n\", .{});\n continue :xsw 2;\n },\n 2 => continue :xsw 3,\n 3 => return,\n 4 => {},\n else => {\n try stdout.print(\n \"Unmatched case, value: {d}\\n\", .{@as(u8, 1)}\n );\n },\n}\n\n\n\n\n2.1.3 The defer keyword\nWith the defer keyword you can register an expression to be executed when you exit the current scope. Therefore, this keyword has a similar functionality as the on.exit() function from R. Take the foo() function below as an example. When we execute this foo() function, the expression that prints the message “Exiting function …” is getting executed only when the function exits its scope.\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn foo() !void {\n defer std.debug.print(\n \"Exiting function ...\\n\", .{}\n );\n try stdout.print(\"Adding some numbers ...\\n\", .{});\n const x = 2 + 2; _ = x;\n try stdout.print(\"Multiplying ...\\n\", .{});\n const y = 2 * 8; _ = y;\n}\n\npub fn main() !void {\n try foo();\n}\n\nAdding some numbers ...\nMultiplying ...\nExiting function ...\nTherefore, we can use defer to declare an expression that is going to be executed when your code exits the current scope. Some programmers like to interpret the phrase “exit of the current scope” as “the end of the current scope”. But this interpretation might not be entirely correct, depending on what you consider as “the end of the current scope”.\nI mean, what do you consider as the end of the current scope? Is it the closing curly bracket (}) of the scope? Is it when the last expression in the function get’s executed? Is it when the function returns to the previous scope? Etc. For example, it would not be correct to interpret the “exit of the current scope” as the closing curly bracket of the scope. Because the function might exit from an earlier position than this closing curly bracket (e.g. an error value was generated at a previous line inside the function; the function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.\nNow, if you remember of what we have discussed in Section 1.7, there are multiple structures in the language that create their own separate scopes. For/while loops, if/else statements, functions, normal blocks, etc. This also affects the interpretation of defer. For example, if you use defer inside a for loop, then, the given expression will be executed everytime this specific for loop exits its own scope.\nBefore we continue, is worth emphasizing that the defer keyword is an “unconditional defer”. Which means that the given expression will be executed no matter how the code exits the current scope. For example, your code might exit the current scope because of an error value being generated, or, because of a return statement, or, a break statement, etc.\n\n\n2.1.4 The errdefer keyword\nOn the previous section, we have discussed the defer keyword, which you can use to register an expression to be executed at the exit of the current scope. But this keyword has a brother, which is the errdefer keyword. While defer is an “unconditional defer”, the errdefer keyword is a “conditional defer”. Which means that the given expression is executed only when you exit the current scope on a very specific circumstance.\nIn more details, the expression given to errdefer is executed only when an error occurs in the current scope. Therefore, if the function (or for/while loop, if/else statement, etc.) exits the current scope in a normal situation, without errors, the expression given to errdefer is not executed.\nThis makes the errdefer keyword one of the many tools available in Zig for error handling. In this section, we are more concerned with the control flow aspects around errdefer. But we are going to discuss errdefer later as a error handling tool in Section 10.2.4.\nThe code example below demonstrates three things:\n\nthat defer is an “unconditional defer”, because the given expression get’s executed regardless of how the function foo() exits its own scope.\nthat errdefer is executed because the function foo() returned an error value.\nthat defer and errdefer expressions are executed in a LIFO (last in, first out) order.\n\n\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n defer i = 2;\n try foo();\n}\n\nValue of i: 2\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\nWhen I say that “defer expressions” are executed in a LIFO order, what I want to say is that the last defer or errdefer expressions in the code are the first ones to be executed. You could also interpret this as: “defer expressions” are executed from bottom to top, or, from last to first.\nTherefore, if I change the order of the defer and errdefer expressions, you will notice that the value of i that get’s printed to the console changes to 1. This doesn’t mean that the defer expression was not executed in this case. This actually means that the defer expression was executed only after the errdefer expression. The code example below demonstrates this:\n\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n defer i = 2;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n try foo();\n}\n\nValue of i: 1\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n\n\n2.1.5 For loops\nA loop allows you to execute the same lines of code multiple times, thus, creating a “repetition space” in the execution flow of your program. Loops are particularly useful when we want to replicate the same function (or the same set of commands) over different inputs.\nThere are different types of loops available in Zig. But the most essential of them all is probably the for loop. A for loop is used to apply the same piece of code over the elements of a slice, or, an array.\nFor loops in Zig use a syntax that may be unfamiliar to programmers coming from other languages. You start with the for keyword, then, you list the items that you want to iterate over inside a pair of parentheses. Then, inside of a pair of pipes (|) you should declare an identifier that will serve as your iterator, or, the “repetition index of the loop”.\n\nfor (items) |value| {\n // code to execute\n}\n\nTherefore, instead of using a (value in items) syntax, in Zig, for loops use the syntax (items) |value|. In the example below, you can see that we are looping through the items of the array stored at the object name, and printing to the console the decimal representation of each character in this array.\nIf we wanted, we could also iterate through a slice (or a portion) of the array, instead of iterating through the entire array stored in the name object. Just use a range selector to select the section you want. For example, I could provide the expression name[0..3] to the for loop, to iterate just through the first 3 elements in the array.\n\nconst name = [_]u8{'P','e','d','r','o'};\nfor (name) |char| {\n try stdout.print(\"{d} | \", .{char});\n}\n\n80 | 101 | 100 | 114 | 111 | \n\n\nIn the above example we are using the value itself of each element in the array as our iterator. But there are many situations where we need to use an index instead of the actual values of the items.\nYou can do that by providing a second set of items to iterate over. More precisely, you provide the range selector 0.. to the for loop. So, yes, you can use two different iterators at the same time in a for loop in Zig.\nBut remember from Section 1.4 that, every object you create in Zig must be used in some way. So if you declare two iterators in your for loop, you must use both iterators inside the for loop body. But if you want to use just the index iterator, and not use the “value iterator”, then, you can discard the value iterator by maching the value items to the underscore character, like in the example below:\n\nconst name = \"Pedro\";\nfor (name, 0..) |_, i| {\n try stdout.print(\"{d} | \", .{i});\n}\n\n0 | 1 | 2 | 3 | 4 |\n\n\n2.1.6 While loops\nA while loop is created from the while keyword. A for loop iterates through the items of an array, but a while loop will loop continuously, and infinitely, until a logical test (specified by you) becomes false.\nYou start with the while keyword, then, you define a logical expression inside a pair of parentheses, and the body of the loop is provided inside a pair of curly braces, like in the example below:\n\nvar i: u8 = 1;\nwhile (i < 5) {\n try stdout.print(\"{d} | \", .{i});\n i += 1;\n}\n\n1 | 2 | 3 | 4 | \n\n\nYou can also specify the increment expression to be used at the beginning of a while loop. To do that, we write the increment expression inside a pair of parentheses after a colon character (:). The code example below demonstrates this other pattern.\n\nvar i: u8 = 1;\nwhile (i < 5) : (i += 1) {\n try stdout.print(\"{d} | \", .{i});\n}\n\n1 | 2 | 3 | 4 | \n\n\n\n\n2.1.7 Using break and continue\nIn Zig, you can explicitly stop the execution of a loop, or, jump to the next iteration of the loop, by using the keywords break and continue, respectively. The while loop presented in the next code example is, at first sight, an infinite loop. Because the logical value inside the parenthese will always be equal to true. But what makes this while loop stop when the i object reaches the count 10? It is the break keyword!\nInside the while loop, we have an if statement that is constantly checking if the i variable is equal to 10. Since we are incrementing the value of i at each iteration of the while loop, this i object will eventually be equal to 10, and when it is, the if statement will execute the break expression, and, as a result, the execution of the while loop is stopped.\nNotice the use of the expect() function from the Zig Standard Library after the while loop. This expect() function is an “assert” type of function. This function checks if the logical test provided is equal to true. If so, the function do nothing. Otherwise (i.e. the logical test is equal to false), the function raises an assertion error.\n\nvar i: usize = 0;\nwhile (true) {\n if (i == 10) {\n break;\n }\n i += 1;\n}\ntry std.testing.expect(i == 10);\ntry stdout.print(\"Everything worked!\", .{});\n\nEverything worked!\n\n\nSince this code example was executed successfully by the zig compiler, without raising any errors, we known that, after the execution of the while loop, the i object is equal to 10. Because if it wasn’t equal to 10, an error would have been raised by expect().\nNow, in the next example, we have a use case for the continue keyword. The if statement is constantly checking if the current index is a multiple of 2. If it is, we jump to the next iteration of the loop. Otherwise, the loop just prints the current index to the console.\n\nconst ns = [_]u8{1,2,3,4,5,6};\nfor (ns) |i| {\n if ((i % 2) == 0) {\n continue;\n }\n try stdout.print(\"{d} | \", .{i});\n}\n\n1 | 3 | 5 |",
+ "text": "2.1.1 If/else statements\nAn if/else statement performs a “conditional flow operation”. A conditional flow control (or choice control) allows you to execute or ignore a certain block of commands based on a logical condition. Many programmers and computer science professionals also use the term “branching” in this case. In essence, an if/else statement allow us to use the result of a logical test to decide whether or not to execute a given block of commands.\nIn Zig, we write if/else statements by using the keywords if and else. We start with the if keyword followed by a logical test inside a pair of parentheses, followed by a pair of curly braces which contains the lines of code to be executed in case the logical test returns the value true.\nAfter that, you can optionally add an else statement. To do that, just add the else keyword followed by a pair of curly braces, with the lines of code to executed in case the logical test defined at if returns false.\nIn the example below, we are testing if the object x contains a number that is greater than 10. Judging by the output printed to the console, we know that this logical test returned false. Because the output in the console is compatible with the line of code present in the else branch of the if/else statement.\n\nconst x = 5;\nif (x > 10) {\n try stdout.print(\n \"x > 10!\\n\", .{}\n );\n} else {\n try stdout.print(\n \"x <= 10!\\n\", .{}\n );\n}\n\nx <= 10!\n\n\n\n\n2.1.2 Switch statements\nSwitch statements are also available in Zig, and they have a very similar syntax to a switch statement in Rust. As you would expect, to write a switch statement in Zig we use the switch keyword. We provide the value that we want to “switch over” inside a pair of parentheses. Then, we list the possible combinations (or “branches”) inside a pair of curly braces.\nLet’s take a look at the code example below. You can see that I’m creating an enum type called Role. We talk more about enums in Section 7.6. But in summary, this Role type is listing different types of roles in a fictitious company, like SE for Software Engineer, DE for Data Engineer, PM for Product Manager, etc.\nNotice that we are using the value from the role object in the switch statement, to discover which exact area we need to store in the area variable object. Also notice that we are using type inference inside the switch statement, with the dot character, as we are going to describe in Section 2.4. This makes the zig compiler infer the correct data type of the values (PM, SE, etc.) for us.\nAlso notice that, we are grouping multiple values in the same branch of the switch statement. We just separate each possible value with a comma. For example, if role contains either DE or DA, the area variable would contain the value \"Data & Analytics\", instead of \"Platform\" or \"Sales\".\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nconst Role = enum {\n SE, DPE, DE, DA, PM, PO, KS\n};\n\npub fn main() !void {\n var area: []const u8 = undefined;\n const role = Role.SE;\n switch (role) {\n .PM, .SE, .DPE, .PO => {\n area = \"Platform\";\n },\n .DE, .DA => {\n area = \"Data & Analytics\";\n },\n .KS => {\n area = \"Sales\";\n },\n }\n try stdout.print(\"{s}\\n\", .{area});\n}\n\nPlatform\n\n\n\n2.1.2.1 Switch statements must exhaust all possibilities\nOne very important aspect about switch statements in Zig is that they must exhaust all existing possibilities. In other words, all possible values that could be found inside the role object must be explicitly handled in this switch statement.\nSince the role object has type Role, the only possible values to be found inside this object are PM, SE, DPE, PO, DE, DA and KS. There are no other possible values to be stored in this role object. Thus, the switch statements must have a combination (branch) for each one of these values. This is what “exhaust all existing possibilities” means. The switch statement covers every possible case.\nTherefore, you cannot write a switch statement in Zig, and leave an edge case with no explicit action to be taken. This is a similar behaviour to switch statements in Rust, which also have to handle all possible cases.\n\n\n2.1.2.2 The else branch\nTake a look at the dump_hex_fallible() function below as an example. This function comes from the Zig Standard Library. More precisely, from the debug.zig module1. There are multiple lines in this function, but I omitted them to focus solely on the switch statement found in this function. Notice that this switch statement has four possible cases (i.e. four explicit branches). Also, notice that we used an else branch in this case.\nAn else branch in a switch statement works as the “default branch”. Whenever you have multiple cases in your switch statement where you want to apply the exact same action, you can use an else branch to do that.\n\npub fn dump_hex_fallible(bytes: []const u8) !void {\n // Many lines ...\n switch (byte) {\n '\\n' => try writer.writeAll(\"␊\"),\n '\\r' => try writer.writeAll(\"␍\"),\n '\\t' => try writer.writeAll(\"␉\"),\n else => try writer.writeByte('.'),\n }\n}\n\nMany programmers would also use an else branch to handle a “not supported” case. That is, a case that cannot be properly handled by your code, or, just a case that should not be “fixed”. Therefore, you can use an else branch to panic (or raise an error) in your program to stop the current execution.\nTake the code example below. We can see that, we are handling the cases for the level object being either 1, 2, or 3. All other possible cases are not supported by default, and, as consequence, we raise a runtime error in such cases through the @panic() built-in function.\nAlso notice that, we are assigning the result of the switch statement to a new object called category. This is another thing that you can do with switch statements in Zig. If a branch outputs a value as result, you can store the result value of the switch statement into a new object.\n\nconst level: u8 = 4;\nconst category = switch (level) {\n 1, 2 => \"beginner\",\n 3 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n\nthread 13103 panic: Not supported level!\nt.zig:9:13: 0x1033c58 in main (switch2)\n @panic(\"Not supported level!\");\n ^\n\n\n2.1.2.3 Using ranges in switch\nFurthermore, you can also use ranges of values in switch statements. That is, you can create a branch in your switch statement that is used whenever the input value is within the specified range. These “range expressions” are created with the operator .... It is important to emphasize that the ranges created by this operator are inclusive on both ends.\nFor example, I could easily change the previous code example to support all levels between 0 and 100. Like this:\n\nconst level: u8 = 4;\nconst category = switch (level) {\n 0...25 => \"beginner\",\n 26...75 => \"intermediary\",\n 76...100 => \"professional\",\n else => {\n @panic(\"Not supported level!\");\n },\n};\ntry stdout.print(\"{s}\\n\", .{category});\n\nbeginner\n\n\nThis is neat, and it works with character ranges too. That is, I could simply write 'a'...'z', to match any character value that is a lowercase letter, and it would work fine.\n\n\n2.1.2.4 Labeled switch statements\nIn Section 1.7 we have talked about labeling blocks, and also, about using these labels to return a value from the block. Well, from version 0.14.0 and onwards of the zig compiler, you can also apply labels over switch statements, which makes it possible to almost implement a “C goto” like pattern.\nFor example, if you give the label xsw to a switch statement, you can use this label in conjunction with the continue keyword to go back to the beginning of the switch statement. In the example below, the execution goes back to the beginning of the switch statement two times, before ending at the 3 branch.\n\nxsw: switch (@as(u8, 1)) {\n 1 => {\n try stdout.print(\"First branch\\n\", .{});\n continue :xsw 2;\n },\n 2 => continue :xsw 3,\n 3 => return,\n 4 => {},\n else => {\n try stdout.print(\n \"Unmatched case, value: {d}\\n\", .{@as(u8, 1)}\n );\n },\n}\n\n\n\n\n2.1.3 The defer keyword\nWith the defer keyword you can register an expression to be executed when you exit the current scope. Therefore, this keyword has a similar functionality as the on.exit() function from R. Take the foo() function below as an example. When we execute this foo() function, the expression that prints the message “Exiting function …” is getting executed only when the function exits its scope.\n\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn foo() !void {\n defer std.debug.print(\n \"Exiting function ...\\n\", .{}\n );\n try stdout.print(\"Adding some numbers ...\\n\", .{});\n const x = 2 + 2; _ = x;\n try stdout.print(\"Multiplying ...\\n\", .{});\n const y = 2 * 8; _ = y;\n}\n\npub fn main() !void {\n try foo();\n}\n\nAdding some numbers ...\nMultiplying ...\nExiting function ...\nTherefore, we can use defer to declare an expression that is going to be executed when your code exits the current scope. Some programmers like to interpret the phrase “exit of the current scope” as “the end of the current scope”. But this interpretation might not be entirely correct, depending on what you consider as “the end of the current scope”.\nI mean, what do you consider as the end of the current scope? Is it the closing curly bracket (}) of the scope? Is it when the last expression in the function get’s executed? Is it when the function returns to the previous scope? Etc. For example, it would not be correct to interpret the “exit of the current scope” as the closing curly bracket of the scope. Because the function might exit from an earlier position than this closing curly bracket (e.g. an error value was generated at a previous line inside the function; the function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.\nNow, if you remember of what we have discussed in Section 1.7, there are multiple structures in the language that create their own separate scopes. For/while loops, if/else statements, functions, normal blocks, etc. This also affects the interpretation of defer. For example, if you use defer inside a for loop, then, the given expression will be executed everytime this specific for loop exits its own scope.\nBefore we continue, is worth emphasizing that the defer keyword is an “unconditional defer”. Which means that the given expression will be executed no matter how the code exits the current scope. For example, your code might exit the current scope because of an error value being generated, or, because of a return statement, or, a break statement, etc.\n\n\n2.1.4 The errdefer keyword\nOn the previous section, we have discussed the defer keyword, which you can use to register an expression to be executed at the exit of the current scope. But this keyword has a brother, which is the errdefer keyword. While defer is an “unconditional defer”, the errdefer keyword is a “conditional defer”. Which means that the given expression is executed only when you exit the current scope on a very specific circumstance.\nIn more details, the expression given to errdefer is executed only when an error occurs in the current scope. Therefore, if the function (or for/while loop, if/else statement, etc.) exits the current scope in a normal situation, without errors, the expression given to errdefer is not executed.\nThis makes the errdefer keyword one of the many tools available in Zig for error handling. In this section, we are more concerned with the control flow aspects around errdefer. But we are going to discuss errdefer later as a error handling tool in Section 10.2.4.\nThe code example below demonstrates three things:\n\nthat defer is an “unconditional defer”, because the given expression get’s executed regardless of how the function foo() exits its own scope.\nthat errdefer is executed because the function foo() returned an error value.\nthat defer and errdefer expressions are executed in a LIFO (last in, first out) order.\n\n\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n defer i = 2;\n try foo();\n}\n\nValue of i: 2\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\nWhen I say that “defer expressions” are executed in a LIFO order, what I want to say is that the last defer or errdefer expressions in the code are the first ones to be executed. You could also interpret this as: “defer expressions” are executed from bottom to top, or, from last to first.\nTherefore, if I change the order of the defer and errdefer expressions, you will notice that the value of i that get’s printed to the console changes to 1. This doesn’t mean that the defer expression was not executed in this case. This actually means that the defer expression was executed only after the errdefer expression. The code example below demonstrates this:\n\nconst std = @import(\"std\");\nfn foo() !void { return error.FooError; }\npub fn main() !void {\n var i: usize = 1;\n defer i = 2;\n errdefer std.debug.print(\"Value of i: {d}\\n\", .{i});\n try foo();\n}\n\nValue of i: 1\nerror: FooError\n/t.zig:6:5: 0x1037e48 in foo (defer)\n return error.FooError;\n ^\n\n\n2.1.5 For loops\nA loop allows you to execute the same lines of code multiple times, thus, creating a “repetition space” in the execution flow of your program. Loops are particularly useful when we want to replicate the same function (or the same set of commands) over different inputs.\nThere are different types of loops available in Zig. But the most essential of them all is probably the for loop. A for loop is used to apply the same piece of code over the elements of a slice, or, an array.\nFor loops in Zig use a syntax that may be unfamiliar to programmers coming from other languages. You start with the for keyword, then, you list the items that you want to iterate over inside a pair of parentheses. Then, inside of a pair of pipes (|) you should declare an identifier that will serve as your iterator, or, the “repetition index of the loop”.\n\nfor (items) |value| {\n // code to execute\n}\n\nTherefore, instead of using a (value in items) syntax, in Zig, for loops use the syntax (items) |value|. In the example below, you can see that we are looping through the items of the array stored at the object name, and printing to the console the decimal representation of each character in this array.\nIf we wanted, we could also iterate through a slice (or a portion) of the array, instead of iterating through the entire array stored in the name object. Just use a range selector to select the section you want. For example, I could provide the expression name[0..3] to the for loop, to iterate just through the first 3 elements in the array.\n\nconst name = [_]u8{'P','e','d','r','o'};\nfor (name) |char| {\n try stdout.print(\"{d} | \", .{char});\n}\n\n80 | 101 | 100 | 114 | 111 | \n\n\nIn the above example we are using the value itself of each element in the array as our iterator. But there are many situations where we need to use an index instead of the actual values of the items.\nYou can do that by providing a second set of items to iterate over. More precisely, you provide the range selector 0.. to the for loop. So, yes, you can use two different iterators at the same time in a for loop in Zig.\nBut remember from Section 1.4 that, every object you create in Zig must be used in some way. So if you declare two iterators in your for loop, you must use both iterators inside the for loop body. But if you want to use just the index iterator, and not use the “value iterator”, then, you can discard the value iterator by maching the value items to the underscore character, like in the example below:\n\nconst name = \"Pedro\";\nfor (name, 0..) |_, i| {\n try stdout.print(\"{d} | \", .{i});\n}\n\n0 | 1 | 2 | 3 | 4 |\n\n\n2.1.6 While loops\nA while loop is created from the while keyword. A for loop iterates through the items of an array, but a while loop will loop continuously, and infinitely, until a logical test (specified by you) becomes false.\nYou start with the while keyword, then, you define a logical expression inside a pair of parentheses, and the body of the loop is provided inside a pair of curly braces, like in the example below:\n\nvar i: u8 = 1;\nwhile (i < 5) {\n try stdout.print(\"{d} | \", .{i});\n i += 1;\n}\n\n1 | 2 | 3 | 4 | \n\n\nYou can also specify the increment expression to be used at the beginning of a while loop. To do that, we write the increment expression inside a pair of parentheses after a colon character (:). The code example below demonstrates this other pattern.\n\nvar i: u8 = 1;\nwhile (i < 5) : (i += 1) {\n try stdout.print(\"{d} | \", .{i});\n}\n\n1 | 2 | 3 | 4 | \n\n\n\n\n2.1.7 Using break and continue\nIn Zig, you can explicitly stop the execution of a loop, or, jump to the next iteration of the loop, by using the keywords break and continue, respectively. The while loop presented in the next code example is, at first sight, an infinite loop. Because the logical value inside the parenthese will always be equal to true. But what makes this while loop stop when the i object reaches the count 10? It is the break keyword!\nInside the while loop, we have an if statement that is constantly checking if the i variable is equal to 10. Since we are incrementing the value of i at each iteration of the while loop, this i object will eventually be equal to 10, and when it is, the if statement will execute the break expression, and, as a result, the execution of the while loop is stopped.\nNotice the use of the expect() function from the Zig Standard Library after the while loop. This expect() function is an “assert” type of function. This function checks if the logical test provided is equal to true. If so, the function do nothing. Otherwise (i.e. the logical test is equal to false), the function raises an assertion error.\n\nvar i: usize = 0;\nwhile (true) {\n if (i == 10) {\n break;\n }\n i += 1;\n}\ntry std.testing.expect(i == 10);\ntry stdout.print(\"Everything worked!\", .{});\n\nEverything worked!\n\n\nSince this code example was executed successfully by the zig compiler, without raising any errors, we known that, after the execution of the while loop, the i object is equal to 10. Because if it wasn’t equal to 10, an error would have been raised by expect().\nNow, in the next example, we have a use case for the continue keyword. The if statement is constantly checking if the current index is a multiple of 2. If it is, we jump to the next iteration of the loop. Otherwise, the loop just prints the current index to the console.\n\nconst ns = [_]u8{1,2,3,4,5,6};\nfor (ns) |i| {\n if ((i % 2) == 0) {\n continue;\n }\n try stdout.print(\"{d} | \", .{i});\n}\n\n1 | 3 | 5 |",
"crumbs": [
"2Control flow, structs, modules and types"
]
@@ -274,7 +274,7 @@
"href": "Chapters/03-structs.html#sec-type-cast",
"title": "2 Control flow, structs, modules and types",
"section": "2.5 Type casting",
- "text": "2.5 Type casting\nIn this section, I want to discuss type casting (or, type conversion) with you. We use type casting when we have an object of type “x”, and we want to convert it into an object of type “y”, i.e. we want to change the data type of the object.\nMost languages have a formal way to perform type casting. In Rust for example, we normally use the keyword as, and in C, we normally use the type casting syntax, e.g. (int) x. In Zig, we use the @as() built-in function to cast an object of type “x”, into an object of type “y”.\nThis @as() function is the preferred way to perform type conversion (or type casting) in Zig. Because it is explicit, and, it also performs the casting only if it is unambiguous and safe. To use this function, you just provide the target data type in the first argument, and, the object that you want cast as the second argument.\n\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 500;\n const y = @as(u32, x);\n try expect(@TypeOf(y) == u32);\n}\n\n1/1 file10b91234368fa.test_0...OKAll 1 tests passe\n ed.\n\n\nThis is the general way to perform type casting in Zig. But remember, @as() works only when casting is unambiguous and safe, and there are situations where these assumptions do not hold. For example, when casting an integer value into a float value, or vice-versa, it is not clear to the compiler how to perform this conversion safely.\nTherefore, we need to use specialized “casting functions” in such situations. For example, if you want to cast an integer value into a float value, then, you should use the @floatFromInt() function. In the inverse scenario, you should use the @intFromFloat() function.\nIn these functions, you just provide the object that you want to cast as input. Then, the target data type of the “type casting operation” is determined by the type annotation of the object where you are saving the results. In the example below, we are casting the object x into a value of type f32, because the object y, which is where we are saving the results, is annotated as an object of type f32.\n\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 565;\n const y: f32 = @floatFromInt(x);\n try expect(@TypeOf(y) == f32);\n}\n\n1/1 file10b9130ae9221.test_0...OKAll 1 tests passe\n ed.\n\n\nAnother built-in function that is very useful when performing type casting operations is @ptrCast(). In essence, we use the @as() built-in function when we want to explicit convert (or cast) a Zig value/object from a type “x” to a type “y”, etc. However, pointers (we are going to discuss pointers in more depth in Chapter 6) are a special type of object in Zig, i.e. they are treated differently from “normal objects”.\nEverytime a pointer is involved in some “type casting operation” in Zig, the @ptrCast() function is used. This function works similarly to @floatFromInt(). You just provide the pointer object that you want to cast as input to this function, and the target data type is, once again, determined by the type annotation of the object where the results are being stored.\n\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const bytes align(@alignOf(u32)) = [_]u8{\n 0x12, 0x12, 0x12, 0x12\n };\n const u32_ptr: *const u32 = @ptrCast(&bytes);\n try expect(@TypeOf(u32_ptr) == *const u32);\n}\n\n1/1 file10b914c434861.test_0...OKAll 1 tests passe\n ed.",
+ "text": "2.5 Type casting\nIn this section, I want to discuss type casting (or, type conversion) with you. We use type casting when we have an object of type “x”, and we want to convert it into an object of type “y”, i.e. we want to change the data type of the object.\nMost languages have a formal way to perform type casting. In Rust for example, we normally use the keyword as, and in C, we normally use the type casting syntax, e.g. (int) x. In Zig, we use the @as() built-in function to cast an object of type “x”, into an object of type “y”.\nThis @as() function is the preferred way to perform type conversion (or type casting) in Zig. Because it is explicit, and, it also performs the casting only if it is unambiguous and safe. To use this function, you just provide the target data type in the first argument, and, the object that you want cast as the second argument.\n\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 500;\n const y = @as(u32, x);\n try expect(@TypeOf(y) == u32);\n}\n\n1/1 filecdc41797a754.test_0...OKAll 1 tests passed\n d.\n\n\nThis is the general way to perform type casting in Zig. But remember, @as() works only when casting is unambiguous and safe, and there are situations where these assumptions do not hold. For example, when casting an integer value into a float value, or vice-versa, it is not clear to the compiler how to perform this conversion safely.\nTherefore, we need to use specialized “casting functions” in such situations. For example, if you want to cast an integer value into a float value, then, you should use the @floatFromInt() function. In the inverse scenario, you should use the @intFromFloat() function.\nIn these functions, you just provide the object that you want to cast as input. Then, the target data type of the “type casting operation” is determined by the type annotation of the object where you are saving the results. In the example below, we are casting the object x into a value of type f32, because the object y, which is where we are saving the results, is annotated as an object of type f32.\n\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const x: usize = 565;\n const y: f32 = @floatFromInt(x);\n try expect(@TypeOf(y) == f32);\n}\n\n1/1 filecdc428638149.test_0...OKAll 1 tests passed\n d.\n\n\nAnother built-in function that is very useful when performing type casting operations is @ptrCast(). In essence, we use the @as() built-in function when we want to explicit convert (or cast) a Zig value/object from a type “x” to a type “y”, etc. However, pointers (we are going to discuss pointers in more depth in Chapter 6) are a special type of object in Zig, i.e. they are treated differently from “normal objects”.\nEverytime a pointer is involved in some “type casting operation” in Zig, the @ptrCast() function is used. This function works similarly to @floatFromInt(). You just provide the pointer object that you want to cast as input to this function, and the target data type is, once again, determined by the type annotation of the object where the results are being stored.\n\nconst std = @import(\"std\");\nconst expect = std.testing.expect;\ntest {\n const bytes align(@alignOf(u32)) = [_]u8{\n 0x12, 0x12, 0x12, 0x12\n };\n const u32_ptr: *const u32 = @ptrCast(&bytes);\n try expect(@TypeOf(u32_ptr) == *const u32);\n}\n\n1/1 filecdc46f71cc03.test_0...OKAll 1 tests passed\n d.",
"crumbs": [
"2Control flow, structs, modules and types"
]
From 91ebf325e129c8bc4666aab2b336cf3b6007b191 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 08:37:05 -0400
Subject: [PATCH 034/151] fix: change his to its
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 0c4de856..c2d329a5 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -121,7 +121,7 @@ For example, the function `input_length()` contains an argument named `input`, w
It is impossible to know the value of this particular argument at compile time. And it also is impossible to know the size/length
of this particular argument. Because it is an array that do not have a fixed size specified explicitly in the argument type annotation.
-So, we know that this `input` argument will be an array of `u8` integers. But we do not know at compile-time, its value, and neither his size.
+So, we know that this `input` argument will be an array of `u8` integers. But we do not know at compile-time, its value, and neither its size.
This information is known only at runtime, which is the period of time when you program is executed.
As a consequence, the value of the expression `input.len` is also known only at runtime.
This is an intrinsic characteristic of any function. Just remember that the value of function arguments is usually not "compile-time known".
From c30ce1c4e62a6b498eae7ff62b7276f61063f758 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 08:43:29 -0400
Subject: [PATCH 035/151] fix: remove apostrophe
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index c2d329a5..8d3678f7 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -151,7 +151,7 @@ It simply exists in your program.
### Stack vs Heap
-If you are familiar with system's programming, or just low-level programming in general, you
+If you are familiar with systems programming, or just low-level programming in general, you
probably have heard of the "duel" between Stack vs Heap. These are two different types of memory,
or different memory spaces, which are both available in Zig.
From a62e7a1ee12cac32f046cddfa7c8cde126eeff62 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 08:49:19 -0400
Subject: [PATCH 036/151] fix: change have to has
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 8d3678f7..fd8870d0 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -242,7 +242,7 @@ freed/destroyed at the end of the function scope.
:::
-This same logic applies to any other special structure in Zig that have its own scope by surrounding
+This same logic applies to any other special structure in Zig that has its own scope by surrounding
it with curly braces (`{}`).
For loops, while loops, if else statements, etc. For example, if you declare any local
object in the scope of a for loop, this local object is accessible only within the scope
From f152e22ee31d23abdfe05f0d6627101203548669 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 08:53:14 -0400
Subject: [PATCH 037/151] fix: change very to well
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index fd8870d0..91c90b57 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -314,7 +314,7 @@ pub fn main() !void {
}
```
-This "invalid pointer to stack variable" problem is very known across many programming language communities.
+This "invalid pointer to stack variable" problem is well known across many programming language communities.
If you try to do the same thing, for example, in a C or C++ program (i.e. returning an address to
a local object stored in the stack), you would also get undefined behaviour
in the program.
From 4301b0d69a98b2819dabd6efcb824e52dddd5bde Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 08:56:52 -0400
Subject: [PATCH 038/151] fix: refine wording
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 91c90b57..597b0073 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -327,7 +327,7 @@ is destroyed at the end of its scope.
:::
But what if you really need to use this local object in some way after your function returns?
-How can you do this? The answer is: "in the same you would do if this was a C or C++ program. By returning
+How can you do this? The answer is: "in the same way you would do if this were a C or C++ program. By returning
an address to an object stored in the heap". The heap memory has a much more flexible lifecycle,
and allows you to get a valid pointer to a local object of a function that already returned
from its scope.
From 49bafcdf09be8e6ebef6a3a2759717decda4e028 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:00:01 -0400
Subject: [PATCH 039/151] fix: change sentence to reduce 'or' usage
---
Chapters/01-memory.qmd | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 597b0073..aff78342 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -346,8 +346,8 @@ In summary, a server is a type of application that runs for long periods of time
and that serves (or "deals with") any incoming request that reaches this particular server.
The heap is a good choice for this type of system, mainly because the server does not know upfront
-how many requests it will receive from users, while it is active. It could be one single request,
-or, 5 thousand requests, or, it could also be zero requests.
+how many requests it will receive from users, while it is active. It could be a single request,
+5 thousand requests, or even zero requests.
The server needs to have the ability to allocate and manage its memory according to how many requests it receives.
Another key difference between the stack and the heap, is that the heap is a type
From d50c9da4afe091a91d09e9c59c4fa3b0b35030b7 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:05:19 -0400
Subject: [PATCH 040/151] fix: change 'he' to 'it'
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index aff78342..9fa7a376 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -386,7 +386,7 @@ object you declare is stored:
1. every constant object (`const`) whose value **is known at compile-time** is also stored in the global data section.
1. every object (constant or not) whose length/size **is known at compile time** is stored in the stack space for the current scope.
1. if an object is created with the method `alloc()` or `create()` of an allocator object, this object is stored in the memory space used by this particular allocator object. Most of allocators available in Zig use the heap memory, so, this object is likely stored in the heap (`FixedBufferAllocator()` is an exception to that).
-1. the heap can only be accessed through allocators. If your object was not created through the `alloc()` or `create()` methods of an allocator object, then, he is most certainly not an object stored in the heap.
+1. the heap can only be accessed through allocators. If your object was not created through the `alloc()` or `create()` methods of an allocator object, then, it is most certainly not an object stored in the heap.
## Stack overflows {#sec-stack-overflow}
From d474f213d92cd92299568d02746edc39e9e76c91 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:07:57 -0400
Subject: [PATCH 041/151] fix grammar in overflow explanation
---
Chapters/01-memory.qmd | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 9fa7a376..97c8ac9d 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -405,8 +405,8 @@ a *stack overflow* happens, and your program just crashes as a result of that. I
you attempt to use more space than is available on the stack.
This type of problem is very similar to a *buffer overflow*, i.e. you are trying to use more space
-than is available in the "buffer object". However, a stack overflow always cause your program to crash,
-while a buffer overflow not always cause your program to crash (although it often does).
+than is available in the "buffer object". However, a stack overflow always causes your program to crash,
+while a buffer overflow does not always cause your program to crash (although it often does).
You can see an example of a stack overflow in the example below. We are trying to allocate a very big array of `u64` values
on the stack. You can see below that this program does not run succesfully, because it crashed
From b941732945a3118bfae7286b8c527ce4b990c397 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:09:27 -0400
Subject: [PATCH 042/151] fix: correct spelling of 'successfully' in error
message explanation
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 97c8ac9d..f7cc8ca8 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -409,7 +409,7 @@ than is available in the "buffer object". However, a stack overflow always cause
while a buffer overflow does not always cause your program to crash (although it often does).
You can see an example of a stack overflow in the example below. We are trying to allocate a very big array of `u64` values
-on the stack. You can see below that this program does not run succesfully, because it crashed
+on the stack. You can see below that this program does not run successfully, because it crashed
with a "segmentation fault" error message.
```{zig}
From 7c9ad9545ef250f8228ef002877341ecf23139bd Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:16:04 -0400
Subject: [PATCH 043/151] fix: add 'a' before 'new string'
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index f7cc8ca8..852a1bdb 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -450,7 +450,7 @@ sure that this function/operator will allocate some memory during its execution.
An example is the `allocPrint()` function from the Zig Standard Library. With this function, you can
write a new string using format specifiers. So, this function is, for example, very similar to the function `sprintf()` in C.
-In order to write such new string, the `allocPrint()` function needs to allocate some memory to store the
+In order to write such a new string, the `allocPrint()` function needs to allocate some memory to store the
output string.
That is why, the first argument of this function is an allocator object that you, the user/programmer, gives
From 4da4cbaca835d0cdc8e4c062ba99d01002382960 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:21:41 -0400
Subject: [PATCH 044/151] fix: change 'have' to 'has'
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 852a1bdb..e4368ef1 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -497,7 +497,7 @@ but you don't need to change the function calls to the methods that do the memor
As we described at @sec-stack, everytime you make a function call in Zig,
a space in the stack is reserved for this function call. But the stack
-has a key limitation which is: every object stored in the stack have a
+has a key limitation which is: every object stored in the stack has a
known fixed length.
But in reality, there are two very common instances where this "fixed length limitation" of the stack is a deal braker:
From c576540d86543de691866e52d948488ef7708251 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 09:26:19 -0400
Subject: [PATCH 045/151] fix: grammar in sentence about stack suitability
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index e4368ef1..464b4c5b 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -511,7 +511,7 @@ stack. However, if this object is stored in the heap, then, you can return a poi
end of the function. Because you (the programmer) control the lifetime of any heap memory that you allocate. You decide
when this memory gets destroyed/freed.
-These are common situations where the stack is not good for.
+These are common situations for which the stack is not good.
That is why you need a different memory management strategy to
store these objects inside your function. You need to use
a memory type that can grow together with your objects, or that you
From bca98a7f6006249adb93b6cdc2e56997440b552d Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Fri, 31 Jan 2025 10:23:46 -0400
Subject: [PATCH 046/151] fix: change 'an' to 'a'
---
Chapters/01-memory.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 464b4c5b..1cd29bd9 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -760,7 +760,7 @@ should use `alloc()` and `free()`. But if you need to store just a single item,
then, the `create()` and `destroy()` methods are ideal for you.
In the example below, I'm defining a struct to represent an user of some sort.
-It could be an user for a game, or a software to manage resources, it doesn't mater.
+It could be a user for a game, or software to manage resources, it doesn't mater.
Notice that I use the `create()` method this time, to store a single `User` object
in the program. Also notice that I use the `destroy()` method to free the memory
used by this object at the end of the scope.
From 3656ee4f9225338775946041c4ac9c1c09c2dd96 Mon Sep 17 00:00:00 2001
From: loveholly
Date: Sun, 2 Feb 2025 11:24:33 +0800
Subject: [PATCH 047/151] Grammar issue
---
docs/Chapters/01-zig-weird.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/Chapters/01-zig-weird.html b/docs/Chapters/01-zig-weird.html
index f7360c5b..f46d6fd0 100644
--- a/docs/Chapters/01-zig-weird.html
+++ b/docs/Chapters/01-zig-weird.html
@@ -612,7 +612,7 @@
// It compiles!
From c5ca497a8a3ba7fb13ea88e0dc1d6de551adfca8 Mon Sep 17 00:00:00 2001
From: Dima Budaragin
Date: Sun, 2 Feb 2025 18:48:48 +0000
Subject: [PATCH 048/151] fix typos
---
Chapters/01-zig-weird.qmd | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd
index f0983bf4..5af2822f 100644
--- a/Chapters/01-zig-weird.qmd
+++ b/Chapters/01-zig-weird.qmd
@@ -335,7 +335,7 @@ compilation error will be raised, warning about this mistake.
The `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.
+Zig modules into a portable C ABI library, or, into object files, respectively.
In the case of the `build-exe` command, a binary executable file is created by the `zig`
compiler in the root directory of your project.
@@ -364,7 +364,7 @@ Hello, world!
### Compile and execute at the same time {#sec-compile-run-code}
-On the previous section, I presented the `zig build-exe` command, which
+In 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
@@ -386,7 +386,7 @@ Hello, world!
### Important note for Windows users
First of all, this is a Windows-specific thing, and, therefore, does not apply to other
-operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that
+operating systems, such as Linux and macOS. In summary, if you have a piece of Zig code that
includes some global variables whose initialization rely on runtime resources, then,
you might have some troubles while trying to compile this Zig code on Windows.
@@ -458,7 +458,7 @@ GitHub issues opened at the official Zig repository. More specifically, the issu
### Compiling the entire project {#sec-compile-project}
-Just as I described at @sec-project-files, as our project grows in size and
+Just as I described in @sec-project-files, 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".
@@ -493,7 +493,7 @@ After 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.
+We will talk more about the build system in Zig later in this book.
In the example below, I'm executing the binary executable
named `hello_world` that was generated by the compiler after the
@@ -554,14 +554,14 @@ codebases, such as:
[^bunjs]: .
All these assets are available on GitHub,
-and this is great, because we can use the GitHub search bar in our advantage,
+and this is great, because we can use the GitHub search bar to our advantage,
to find Zig code that fits our description.
For example, you can always include `lang:Zig` in the GitHub search bar when you
are searching for a particular pattern. This will limit the search to only Zig modules.
[^zig-lib-std]:
-Also, a great alternative is to consult online resources and documentations.
+Also, a great alternative is to consult online resources and documentation.
Here is a quick list of resources that I personally use from time to time to learn
more about the language each day:
@@ -583,7 +583,7 @@ fix these programs, and make them work again.
[^ziglings]: .
-A famous tech YouTuber known as *The Primeagen* also posted some videos (at YouTube)
+A famous tech YouTuber known as *The Primeagen* also posted some videos (on YouTube)
where he solves these exercises from Ziglings. The first video is named
["Trying Zig Part 1"](https://www.youtube.com/watch?v=OPuztQfM3Fg&t=2524s&ab_channel=TheVimeagen)[^prime1].
From 254d9b1169bf4b1f643c457fcb0f5a005f4b63c7 Mon Sep 17 00:00:00 2001
From: Alexander Alekhin
Date: Wed, 5 Feb 2025 20:22:08 +0300
Subject: [PATCH 049/151] fix errors in code samples
---
Chapters/01-base64.qmd | 9 ++++-----
Chapters/01-memory.qmd | 6 +++---
Chapters/02-debugging.qmd | 12 ++++++------
3 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/Chapters/01-base64.qmd b/Chapters/01-base64.qmd
index d883696c..fabfc797 100644
--- a/Chapters/01-base64.qmd
+++ b/Chapters/01-base64.qmd
@@ -115,7 +115,7 @@ const Base64 = struct {
};
}
- pub fn _char_at(self: Base64, index: u8) u8 {
+ pub fn _char_at(self: Base64, index: usize) u8 {
return self._table[index];
}
};
@@ -175,7 +175,7 @@ to fill the space that it needs. That is why at @fig-base64-algo1, on the third
2 extra zeros were added to fill the gap in this group.
When we have a 6-bit group that is not completely full, like the third group, extra zeros
-are added to fill the gap. But what about when an entire 6-bit group is empty, or, it
+are added to fill the gap. But what about when an entire 6-bit group is empty, or, it
simply doesn't exist? This is the case of the fourth 6-bit group exposed at
@fig-base64-algo1.
@@ -682,10 +682,9 @@ fn _char_index(self: Base64, char: u8) u8 {
return 64;
var index: u8 = 0;
for (0..63) |i| {
- if (self._char_at(i) == char) {
- index = i;
+ if (self._char_at(i) == char)
break;
- }
+ index += 1;
}
return index;
diff --git a/Chapters/01-memory.qmd b/Chapters/01-memory.qmd
index 0c4de856..94ce0ecb 100644
--- a/Chapters/01-memory.qmd
+++ b/Chapters/01-memory.qmd
@@ -177,7 +177,7 @@ a dynamic memory space, which can grow or shrink to fit the size of your objects
### Stack {#sec-stack}
-The stack is a type of memory that uses the power of the *stack data structure*, hence the name.
+The stack is a type of memory that uses the power of the *stack data structure*, hence the name.
A "stack" is a type of *data structure* that uses a "last in, first out" (LIFO) mechanism to store the values
you give it to. I imagine you are familiar with this data structure.
But, if you are not, the [Wikipedia page](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))[^wiki-stack]
@@ -263,7 +263,7 @@ for (0..a.len) |i| {
// Trying to use an object that was
// declared in the for loop scope,
// and that does not exist anymore.
-std.debug.print("{d}\n", index);
+std.debug.print("{d}\n", .{index});
```
@@ -520,7 +520,7 @@ The heap fits this description.
Allocating memory on the heap is commonly known as dynamic memory management. As the objects you create grow in size
during the execution of your program, you grow the amount of memory
-you have by allocating more memory in the heap to store these objects.
+you have by allocating more memory in the heap to store these objects.
And you do that in Zig, by using an allocator object.
diff --git a/Chapters/02-debugging.qmd b/Chapters/02-debugging.qmd
index 7f3e147a..6329bdea 100644
--- a/Chapters/02-debugging.qmd
+++ b/Chapters/02-debugging.qmd
@@ -94,7 +94,7 @@ fn add(x: u8, y: u8) u8 {
pub fn main() !void {
const result = add(34, 16);
- _ = try stdout.print("Result: {d}", .{result});
+ try stdout.print("Result: {d}", .{result});
}
```
@@ -128,7 +128,7 @@ then, creating a *writer* object to `stderr`, then, using the `print()` method o
const std = @import("std");
const stderr = std.io.getStdErr().writer();
// some more lines ...
-_ = try stderr.print("Result: {d}", .{result});
+try stderr.print("Result: {d}", .{result});
```
@@ -184,7 +184,7 @@ fn add_and_increment(a: u8, b: u8) u8 {
pub fn main() !void {
var n = add_and_increment(2, 3);
n = add_and_increment(n, n);
- _ = try stdout.print("Result: {d}!\n", .{n});
+ try stdout.print("Result: {d}!\n", .{n});
}
```
@@ -227,7 +227,7 @@ Process 8654 stopped
stop reason = breakpoint 1.1 frame #0: 0x10341a6
add_program`debug1.main at add_program.zig:11:30
8 }
- 9
+ 9
10 pub fn main() !void {
-> 11 var n = add_and_increment(2, 3);
12 n = add_and_increment(n, n);
@@ -268,7 +268,7 @@ Process 4798 stopped
* thread #1, name = 'debugging',
stop reason = step over frame #0: 0x10341ae
debugging`debug1.main at debug1.zig:12:26
- 9
+ 9
10 pub fn main() !void {
11 var n = add_and_increment(2, 3);
-> 12 n = add_and_increment(n, n);
@@ -327,7 +327,7 @@ But you also have alternatives embedded in the language itself to access the dat
types of your objects.
In Zig, you can retrieve the data type of an object, by using the built-in function
-`@TypeOf()`. Just apply this function over the object, and you get access to
+`@TypeOf()`. Just apply this function over the object, and you get access to
the data type of the object.
```{zig}
From 85ced25486d2d882b9f5ff8255db8319cbdef685 Mon Sep 17 00:00:00 2001
From: jorge-j1m <68088000+jorge-j1m@users.noreply.github.com>
Date: Fri, 7 Feb 2025 23:58:53 -0500
Subject: [PATCH 050/151] Fix typo in Chapter 1
---
Chapters/01-zig-weird.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd
index f0983bf4..298c6f45 100644
--- a/Chapters/01-zig-weird.qmd
+++ b/Chapters/01-zig-weird.qmd
@@ -335,7 +335,7 @@ compilation error will be raised, warning about this mistake.
The `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.
+Zig modules into a portable C ABI library, or, into object files, respectively.
In the case of the `build-exe` command, a binary executable file is created by the `zig`
compiler in the root directory of your project.
From 37048ebd85bef14d534c5fa7dbb4efb410386c0b Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Sat, 8 Feb 2025 21:43:03 -0400
Subject: [PATCH 051/151] fix: change your to you
---
Chapters/02-debugging.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/02-debugging.qmd b/Chapters/02-debugging.qmd
index 7f3e147a..b4946bfc 100644
--- a/Chapters/02-debugging.qmd
+++ b/Chapters/02-debugging.qmd
@@ -51,7 +51,7 @@ the Zig Standard Library Official Reference for the type `File`](https://ziglang
[^zig-fiile-reference]: .
For our purpose here, which is to write something to the `stdout`, especially to debug our
-program, I recommend you to use the `writer()` method, which gives your a *writer* object.
+program, I recommend you to use the `writer()` method, which gives you a *writer* object.
This *writer* object offers some helper methods to write stuff into the file descriptor object
that represents the `stdout` stream. In special, the `print()` method.
From f27eb4791c87113095573337a9e8e9928afb9405 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Sat, 8 Feb 2025 22:02:58 -0400
Subject: [PATCH 052/151] fix: change print to prints
---
Chapters/02-debugging.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/02-debugging.qmd b/Chapters/02-debugging.qmd
index b4946bfc..cf8614ee 100644
--- a/Chapters/02-debugging.qmd
+++ b/Chapters/02-debugging.qmd
@@ -99,7 +99,7 @@ pub fn main() !void {
```
Is important to emphasize that, the `stdout.print()` method, as you would expect,
-print your template string into the `stdout` stream of your system.
+prints your template string into the `stdout` stream of your system.
However, you can also print your template string into the `stderr` stream
if your prefer. All you need to do, is to replace the `stdout.print()`
call with the function `std.debug.print()`. Like this:
From c6bdd042915772118a08e4edd9de9a26016db156 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Sat, 8 Feb 2025 22:04:41 -0400
Subject: [PATCH 053/151] fix: Add a subject to the sentence
---
Chapters/02-debugging.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/02-debugging.qmd b/Chapters/02-debugging.qmd
index cf8614ee..e5940da3 100644
--- a/Chapters/02-debugging.qmd
+++ b/Chapters/02-debugging.qmd
@@ -98,7 +98,7 @@ pub fn main() !void {
}
```
-Is important to emphasize that, the `stdout.print()` method, as you would expect,
+It is important to emphasize that, the `stdout.print()` method, as you would expect,
prints your template string into the `stdout` stream of your system.
However, you can also print your template string into the `stderr` stream
if your prefer. All you need to do, is to replace the `stdout.print()`
From f81cb7130288c1b390efcfd3a2a6efebb2ca13f5 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Sat, 8 Feb 2025 22:13:30 -0400
Subject: [PATCH 054/151] fix: change being to is
---
Chapters/02-debugging.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/02-debugging.qmd b/Chapters/02-debugging.qmd
index e5940da3..811d5fe3 100644
--- a/Chapters/02-debugging.qmd
+++ b/Chapters/02-debugging.qmd
@@ -135,7 +135,7 @@ _ = try stderr.print("Result: {d}", .{result});
## Debugging through debuggers
-Although *print debugging* being a valid and very useful strategy,
+Although *print debugging* is a valid and very useful strategy,
most programmers prefer to use a debugger to debug their programs.
Since Zig is a low-level language, you can use either GDB (GNU Debugger),
or LLDB (LLVM Project Debugger) as your debugger.
From 77b6549487fa89fba69671cecec2744aac999402 Mon Sep 17 00:00:00 2001
From: hamza wahed
Date: Sat, 8 Feb 2025 22:26:29 -0400
Subject: [PATCH 055/151] fix: grammar in sentence
---
Chapters/02-debugging.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/02-debugging.qmd b/Chapters/02-debugging.qmd
index 811d5fe3..1fddaa70 100644
--- a/Chapters/02-debugging.qmd
+++ b/Chapters/02-debugging.qmd
@@ -255,7 +255,7 @@ by using the `p` LLDB command. The syntax for this command is `p
Date: Sun, 9 Feb 2025 10:50:46 -0300
Subject: [PATCH 056/151] Recompile book with changes
---
_freeze/Chapters/01-zig-weird/execute-results/html.json | 8 +++-----
docs/Chapters/01-zig-weird.html | 2 +-
docs/search.json | 2 +-
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/_freeze/Chapters/01-zig-weird/execute-results/html.json b/_freeze/Chapters/01-zig-weird/execute-results/html.json
index 28800571..49b461d6 100644
--- a/_freeze/Chapters/01-zig-weird/execute-results/html.json
+++ b/_freeze/Chapters/01-zig-weird/execute-results/html.json
@@ -1,11 +1,9 @@
{
- "hash": "507166385ff85ccf512b153b7c107291",
+ "hash": "4ab2f8387f13b3772b2eb726e8e611b5",
"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.\nZig is a very young language that is being actively developed.\nAs a consequence, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 compile successfully. 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 successfully.\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;\n1. 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 successfully 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 objects 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 common 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 {#sec-select-array-elem}\n\nOne very common 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 accompanied 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### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\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 the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. 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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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.write(\n \"Bytes that represents the string object: \"\n );\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();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
- "supporting": [
- "01-zig-weird_files"
- ],
+ "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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\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\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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\n\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\n\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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 portable 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 compile successfully. 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\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\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 successfully.\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\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;\n1. 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 successfully compiled our source code.\n\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\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\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\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 objects 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\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\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 common 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 {#sec-select-array-elem}\n\nOne very common 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\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\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\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\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\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\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\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\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 accompanied 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\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\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\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\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\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\n### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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\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\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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\n\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\n\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\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\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\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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. As an example, the `string_object` object below is 43 bytes long:\n\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\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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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\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.write(\n \"Bytes that represents the string object: \"\n );\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\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 f7360c5b..ff08a55e 100644
--- a/docs/Chapters/01-zig-weird.html
+++ b/docs/Chapters/01-zig-weird.html
@@ -459,7 +459,7 @@
You 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.
zig build-exe src/main.zig
Since 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.
-
The 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.
+
The 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 portable C ABI library, or, into object files, respectively.
In 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.
ls
build.zig build.zig.zon main src
diff --git a/docs/search.json b/docs/search.json
index 9d8728f7..e3d46f3e 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -124,7 +124,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.\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 init 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.\nLow-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.\nIn Zig, we don’t need to use a separate set of tools to build our projects, because a build system is embedded inside the language itself. Therefore, 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 a JSON-like file, in which you can describe your project, and also, declare a set of dependencies of your project that you want to fetch from the internet. In other words, you can use this build.zig.zon file to include a list of external libraries in your project.\nOne possible way to include an external Zig library in your project, is to manually build and install the library in your system, and just link your source code with the library at the build step of your project.\nHowever, if this external Zig library is available on GitHub for example, and it has a valid build.zig.zon file in root folder of the project, which describes the project, you can easily include this library in your project by simply listing this external library in your build.zig.zon file.\nIn other words, this build.zig.zon file works similarly to the package.json file in Javascript projects, or the Pipfile file in Python projects, or the Cargo.toml file in Rust projects. You can read more about this specific file in a couple of articles on the internet23, and you can also see the expected schema for this build.zig.zon file in a documentation file inside the official repository of Zig4.\n\n\n1.2.2 The file root.zig\nLet’s take a look into the root.zig file. You might have noticed that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language5.\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. 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 a strongly-typed language. There are some specific situations where you can (if you want to) omit the type of an object in your code, if this type can be inferred by the zig compiler (we talk more about that at Section 2.4). But there are other situations where you do need to be explicit. For example, you do have to explicitly specify the type of each function argument, and also, the return type of every function that you create in Zig.\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. Therefore, if you are writing a library for other people to use, you have to expose the functions you write in the public API of this library by using this export keyword. 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.\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 there are some other elements that we haven’t 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. The return type of the function (void) is accompanied by an exclamation mark (!). This exclamation mark tells us that this main() function might return an error.\nIs worth noting that, a main() function in Zig is allowed to return nothing (void), or an unsigned 8-bit integer (u8) value6, or an error. In other words, you can write your main() function in Zig to return essentially nothing (void), or, if you prefer, you can also write a more C-like main() function, which returns an integer value that usually serves as a “status code” for the process.\nIn this example, the return type annotation of main() indicates that this function can either return nothing (void), or return an error. This exclamation mark in the return type annotation is an interesting and powerful feature of Zig. In summary, if you write a function and something inside 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 and make it clear that this function might return an error.\nexplicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through a try catch pattern. Zig does have both try and catch keywords. But they work a little differently than what you’re probably used to in other languages.\nIf we look at the main() function below, you can see that we do have a try keyword on the 5th line. But we do not have a catch keyword in this code. In Zig, we use the try keyword to execute an expression that might return an error, which, in this example, is the stdout.print() expression.\nIn essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword does absolutely nothing. It only passes the value forward. It is like if this try keyword was never there. However, if the expression does return an error, then, the try keyword will unwrap the error value, then, it returns this error from the function and also prints the current 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 stop even if you don’t want to stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. It marks the main() function as a public function from this module. Every function in your Zig module is by default private to this Zig module and can only be called from within the module. Unless, you explicitly mark this function as a public function with the pub keyword.\nIf you think about it, this pub keyword in Zig does essentially the opposite of what the static keyword do in C/C++. By making a function “public” you allow other Zig modules to access and call this function. A calling Zig module imports another module by using the @import() built-in function, which makes all public functions from the imported module visible to the calling 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 Important note for Windows users\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.\nAn example of that is accessing the stdout (i.e. the standard output of your system), which is usually done in Zig by using the expression std.io.getStdOut(). Thus, if you use this expression to instantiate a global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows, with a “unable to evaluate comptime expression” error message.\nThis failure in the compilation process happens because all global variables in Zig are initialized at compile-time. However, operations like accessing the stdout (or opening a file) on Windows depends on resources that are available only at runtime (you will learn more about compile-time versus runtime at Section 3.1.1).\nFor example, if you try to compile this code example on Windows, you will likely get the error message exposed below:\n\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\nTo avoid this problem on Windows, we need to force the zig compiler to instantiate this stdout object only at runtime, instead of instantiating it at compile-time. We can achieve that by simply moving the expression to a function body.\nThis solves the problem because all expressions that are inside a function body in Zig are evaluated only at runtime, unless you use the comptime keyword explicitly to change this behaviour. You will learn more about this comptime keyword at Section 12.1.\n\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n\nHello\nYou can read more details about this Windows-specific limitation at a couple of GitHub issues opened at the official Zig repository. More specifically, the issues 17186 7 and 19864 8.\n\n\n1.2.7 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 does 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 init 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.\nLow-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.\nIn Zig, we don’t need to use a separate set of tools to build our projects, because a build system is embedded inside the language itself. Therefore, 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 a JSON-like file, in which you can describe your project, and also, declare a set of dependencies of your project that you want to fetch from the internet. In other words, you can use this build.zig.zon file to include a list of external libraries in your project.\nOne possible way to include an external Zig library in your project, is to manually build and install the library in your system, and just link your source code with the library at the build step of your project.\nHowever, if this external Zig library is available on GitHub for example, and it has a valid build.zig.zon file in root folder of the project, which describes the project, you can easily include this library in your project by simply listing this external library in your build.zig.zon file.\nIn other words, this build.zig.zon file works similarly to the package.json file in Javascript projects, or the Pipfile file in Python projects, or the Cargo.toml file in Rust projects. You can read more about this specific file in a couple of articles on the internet23, and you can also see the expected schema for this build.zig.zon file in a documentation file inside the official repository of Zig4.\n\n\n1.2.2 The file root.zig\nLet’s take a look into the root.zig file. You might have noticed that every line of code with an expression ends with a semicolon (;). This follows the syntax of a C-family programming language5.\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. 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 a strongly-typed language. There are some specific situations where you can (if you want to) omit the type of an object in your code, if this type can be inferred by the zig compiler (we talk more about that at Section 2.4). But there are other situations where you do need to be explicit. For example, you do have to explicitly specify the type of each function argument, and also, the return type of every function that you create in Zig.\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. Therefore, if you are writing a library for other people to use, you have to expose the functions you write in the public API of this library by using this export keyword. 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.\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 there are some other elements that we haven’t 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. The return type of the function (void) is accompanied by an exclamation mark (!). This exclamation mark tells us that this main() function might return an error.\nIs worth noting that, a main() function in Zig is allowed to return nothing (void), or an unsigned 8-bit integer (u8) value6, or an error. In other words, you can write your main() function in Zig to return essentially nothing (void), or, if you prefer, you can also write a more C-like main() function, which returns an integer value that usually serves as a “status code” for the process.\nIn this example, the return type annotation of main() indicates that this function can either return nothing (void), or return an error. This exclamation mark in the return type annotation is an interesting and powerful feature of Zig. In summary, if you write a function and something inside 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 and make it clear that this function might return an error.\nexplicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through a try catch pattern. Zig does have both try and catch keywords. But they work a little differently than what you’re probably used to in other languages.\nIf we look at the main() function below, you can see that we do have a try keyword on the 5th line. But we do not have a catch keyword in this code. In Zig, we use the try keyword to execute an expression that might return an error, which, in this example, is the stdout.print() expression.\nIn essence, the try keyword executes the expression stdout.print(). If this expression returns a valid value, then, the try keyword does absolutely nothing. It only passes the value forward. It is like if this try keyword was never there. However, if the expression does return an error, then, the try keyword will unwrap the error value, then, it returns this error from the function and also prints the current 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 stop even if you don’t want to stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that the main() function is marked with the pub keyword. It marks the main() function as a public function from this module. Every function in your Zig module is by default private to this Zig module and can only be called from within the module. Unless, you explicitly mark this function as a public function with the pub keyword.\nIf you think about it, this pub keyword in Zig does essentially the opposite of what the static keyword do in C/C++. By making a function “public” you allow other Zig modules to access and call this function. A calling Zig module imports another module by using the @import() built-in function, which makes all public functions from the imported module visible to the calling 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 portable 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 Important note for Windows users\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.\nAn example of that is accessing the stdout (i.e. the standard output of your system), which is usually done in Zig by using the expression std.io.getStdOut(). Thus, if you use this expression to instantiate a global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows, with a “unable to evaluate comptime expression” error message.\nThis failure in the compilation process happens because all global variables in Zig are initialized at compile-time. However, operations like accessing the stdout (or opening a file) on Windows depends on resources that are available only at runtime (you will learn more about compile-time versus runtime at Section 3.1.1).\nFor example, if you try to compile this code example on Windows, you will likely get the error message exposed below:\n\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\nTo avoid this problem on Windows, we need to force the zig compiler to instantiate this stdout object only at runtime, instead of instantiating it at compile-time. We can achieve that by simply moving the expression to a function body.\nThis solves the problem because all expressions that are inside a function body in Zig are evaluated only at runtime, unless you use the comptime keyword explicitly to change this behaviour. You will learn more about this comptime keyword at Section 12.1.\n\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n\nHello\nYou can read more details about this Windows-specific limitation at a couple of GitHub issues opened at the official Zig repository. More specifically, the issues 17186 7 and 19864 8.\n\n\n1.2.7 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 does 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": [
"1Introducing Zig"
]
From 9ad203426c28a152275997bcba50fa72b3250231 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 9 Feb 2025 10:51:50 -0300
Subject: [PATCH 057/151] Add change to QMD file
---
Chapters/01-zig-weird.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd
index f0983bf4..12b677e5 100644
--- a/Chapters/01-zig-weird.qmd
+++ b/Chapters/01-zig-weird.qmd
@@ -731,7 +731,7 @@ Everytime you declare a new object in Zig, you have two choices:
1. or you explicitly discard the value of the object;
To explicitly discard the value of any object (constant or variable), all you need to do is to assign
-this object to an special character in Zig, which is the underscore (`_`).
+this object to a special character in Zig, which is the underscore (`_`).
When you assign an object to a underscore, like in the example below, the `zig` compiler will automatically
discard the value of this particular object.
From ba6c7bf1bcb6c7a0357fe79d3adc521567d8c30d Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 9 Feb 2025 10:53:11 -0300
Subject: [PATCH 058/151] Recompile book with changes
---
_freeze/Chapters/01-zig-weird/execute-results/html.json | 8 +++-----
docs/search.json | 2 +-
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/_freeze/Chapters/01-zig-weird/execute-results/html.json b/_freeze/Chapters/01-zig-weird/execute-results/html.json
index 28800571..3f48d096 100644
--- a/_freeze/Chapters/01-zig-weird/execute-results/html.json
+++ b/_freeze/Chapters/01-zig-weird/execute-results/html.json
@@ -1,11 +1,9 @@
{
- "hash": "507166385ff85ccf512b153b7c107291",
+ "hash": "5041be02baff66bca29c690e3dfbfd7c",
"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.\nZig is a very young language that is being actively developed.\nAs a consequence, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 compile successfully. 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 successfully.\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;\n1. 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 successfully 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 objects 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 common 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 {#sec-select-array-elem}\n\nOne very common 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 accompanied 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### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\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 the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. 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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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.write(\n \"Bytes that represents the string object: \"\n );\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();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
- "supporting": [
- "01-zig-weird_files"
- ],
+ "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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\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\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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\n\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\n\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 compile successfully. 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\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\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 successfully.\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\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;\n1. 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 a 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 successfully compiled our source code.\n\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\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\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\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 objects 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\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\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 common 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 {#sec-select-array-elem}\n\nOne very common 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\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\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\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\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\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\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\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\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 accompanied 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\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\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\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\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\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\n### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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\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\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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\n\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\n\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\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\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\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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. As an example, the `string_object` object below is 43 bytes long:\n\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\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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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\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.write(\n \"Bytes that represents the string object: \"\n );\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
+ "supporting": [],
"filters": [
"rmarkdown/pagebreak.lua"
],
diff --git a/docs/search.json b/docs/search.json
index 9d8728f7..805ce92a 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -144,7 +144,7 @@
"href": "Chapters/01-zig-weird.html#sec-assignments",
"title": "1 Introducing Zig",
"section": "1.4 Creating new objects in Zig (i.e. identifiers)",
- "text": "1.4 Creating new objects in Zig (i.e. identifiers)\nLet’s talk more about objects in Zig. Readers that have past experience with other programming languages might know this concept through a different name, such as: “variable” or “identifier”. In this book, I choose to use the term “object” to refer to this concept.\nTo create a new object (or a new “identifier”) in Zig, we use the keywords const or var. These keywords specify if the object that you are creating is mutable or not. If you use const, then the object you are creating is a constant (or immutable) object, which means that once you declare this object, you can no longer change the value stored inside this object.\nOn the other side, if you use var, then, you are creating a variable (or mutable) object. You can change the value of this object as many times you want. Using the keyword var in Zig is similar to using the keywords let mut in Rust.\n\n1.4.1 Constant objects vs variable objects\nIn the code example below, we are creating a new constant object called age. This object stores a number representing the age of someone. However, this code example does not compile successfully. Because on the next line of code, we are trying to change the value of the object age to 25.\nThe zig compiler detects that we are trying to change the value of an object/identifier that is constant, and because of that, the compiler will raise a compilation error, warning us about the mistake.\n\nconst age = 24;\n// The line below is not valid!\nage = 25;\n\nt.zig:10:5: error: cannot assign to constant\n age = 25;\n ~~^~~\nIn contrast, if you use var, then, the object created is a variable object. With var you can declare this object in your source code, and then, change the value of this object how many times you want over future points in your source code.\nSo, using the same code example exposed above, if I change the declaration of the age object to use the var keyword, then, the program gets compiled successfully. Because now, the zig compiler detects that we are changing the value of an object that allows this behaviour, because it is an “variable object”.\n\nvar age: u8 = 24;\nage = 25;\n\n\n\n1.4.2 Declaring without an initial value\nBy default, when you declare a new object in Zig, you must give it an initial value. In other words, this means that we have to declare, and, at the same time, initialize every object we create in our source code.\nOn the other hand, you can, in fact, declare a new object in your source code, and not give it an explicit value. But we need to use a special keyword for that, which is the undefined keyword.\nIs important to emphasize that, you should avoid using undefined as much as possible. Because when you use this keyword, you leave your object uninitialized, and, as a consequence, if for some reason, your code use this object while it is uninitialized, then, you will definitely have undefined behaviour and major bugs in your program.\nIn the example below, I’m declaring the age object again. But this time, I do not give it an initial value. The variable is only initialized at the second line of code, where I store the number 25 in this object.\n\nvar age: u8 = undefined;\nage = 25;\n\nHaving these points in mind, just remember that you should avoid as much as possible to use undefined in your code. Always declare and initialize your objects. Because this gives you much more safety in your program. But in case you really need to declare an object without initializing it… the undefined keyword is the way to do it in Zig.\n\n\n1.4.3 There is no such thing as unused objects\nEvery object (being constant or variable) that you declare in Zig must be used in some way. You can give this object to a function call, as a function argument, or, you can use it in another expression to calculate the value of another object, or, you can call a method that belongs to this particular object.\nIt doesn’t matter in which way you use it. As long as you use it. If you try to break this rule, i.e. if your try to declare a object, but not use it, the zig compiler will not compile your Zig source code, and it will issue a error message warning that you have unused objects in your code.\nLet’s demonstrate this with an example. In the source code below, we declare a constant object called age. If you try to compile a simple Zig program with this line of code below, the compiler will return an error as demonstrated below:\n\nconst age = 15;\n\nt.zig:4:11: error: unused local constant\n const age = 15;\n ^~~\nEverytime you declare a new object in Zig, you have two choices:\n\nyou either use the value of this object;\nor 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 this object to an special character in Zig, which is the underscore (_). When you assign an object to a underscore, like in the example below, the zig compiler will automatically discard the value of this particular object.\nYou can see in the example below that, this time, the compiler did not complain about any “unused constant”, and successfully compiled our source code.\n\n// It compiles!\nconst age = 15;\n_ = age;\n\nNow, remember, everytime you assign a particular object to the underscore, this object is essentially destroyed. It is discarded by the compiler. This means that you can no longer use this object further in your code. It doesn’t exist anymore.\nSo if you try to use the constant age in the example below, after we discarded it, you will get a loud error message from the compiler (talking about a “pointless discard”) warning you about this mistake.\n\n// It does not compile.\nconst age = 15;\n_ = age;\n// Using a discarded value!\nstd.debug.print(\"{d}\\n\", .{age + 2});\n\nt.zig:7:5: error: pointless discard\n of local constant\nThis same rule applies to variable objects. Every variable object must also be used in some way. And if you assign a variable object to the underscore, this object also get’s discarded, and you can no longer use this object.\n\n\n1.4.4 You must mutate every variable objects\nEvery variable object that you create in your source code must be mutated at some point. In other words, if you declare an object as a variable object, with the keyword var, and you do not change the value of this object at some point in the future, the zig compiler will detect this, and it will raise an error warning you about this mistake.\nThe concept behind this is that every object you create in Zig should be preferably a constant object, unless you really need an object whose value will change during the execution of your program.\nSo, if I try to declare a variable object such as where_i_live below, and I do not change the value of this object in some way, the zig compiler raises an error message with the phrase “variable is never mutated”.\n\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n\nt.zig:7:5: error: local variable is never mutated\nt.zig:7:5: note: consider using 'const'",
+ "text": "1.4 Creating new objects in Zig (i.e. identifiers)\nLet’s talk more about objects in Zig. Readers that have past experience with other programming languages might know this concept through a different name, such as: “variable” or “identifier”. In this book, I choose to use the term “object” to refer to this concept.\nTo create a new object (or a new “identifier”) in Zig, we use the keywords const or var. These keywords specify if the object that you are creating is mutable or not. If you use const, then the object you are creating is a constant (or immutable) object, which means that once you declare this object, you can no longer change the value stored inside this object.\nOn the other side, if you use var, then, you are creating a variable (or mutable) object. You can change the value of this object as many times you want. Using the keyword var in Zig is similar to using the keywords let mut in Rust.\n\n1.4.1 Constant objects vs variable objects\nIn the code example below, we are creating a new constant object called age. This object stores a number representing the age of someone. However, this code example does not compile successfully. Because on the next line of code, we are trying to change the value of the object age to 25.\nThe zig compiler detects that we are trying to change the value of an object/identifier that is constant, and because of that, the compiler will raise a compilation error, warning us about the mistake.\n\nconst age = 24;\n// The line below is not valid!\nage = 25;\n\nt.zig:10:5: error: cannot assign to constant\n age = 25;\n ~~^~~\nIn contrast, if you use var, then, the object created is a variable object. With var you can declare this object in your source code, and then, change the value of this object how many times you want over future points in your source code.\nSo, using the same code example exposed above, if I change the declaration of the age object to use the var keyword, then, the program gets compiled successfully. Because now, the zig compiler detects that we are changing the value of an object that allows this behaviour, because it is an “variable object”.\n\nvar age: u8 = 24;\nage = 25;\n\n\n\n1.4.2 Declaring without an initial value\nBy default, when you declare a new object in Zig, you must give it an initial value. In other words, this means that we have to declare, and, at the same time, initialize every object we create in our source code.\nOn the other hand, you can, in fact, declare a new object in your source code, and not give it an explicit value. But we need to use a special keyword for that, which is the undefined keyword.\nIs important to emphasize that, you should avoid using undefined as much as possible. Because when you use this keyword, you leave your object uninitialized, and, as a consequence, if for some reason, your code use this object while it is uninitialized, then, you will definitely have undefined behaviour and major bugs in your program.\nIn the example below, I’m declaring the age object again. But this time, I do not give it an initial value. The variable is only initialized at the second line of code, where I store the number 25 in this object.\n\nvar age: u8 = undefined;\nage = 25;\n\nHaving these points in mind, just remember that you should avoid as much as possible to use undefined in your code. Always declare and initialize your objects. Because this gives you much more safety in your program. But in case you really need to declare an object without initializing it… the undefined keyword is the way to do it in Zig.\n\n\n1.4.3 There is no such thing as unused objects\nEvery object (being constant or variable) that you declare in Zig must be used in some way. You can give this object to a function call, as a function argument, or, you can use it in another expression to calculate the value of another object, or, you can call a method that belongs to this particular object.\nIt doesn’t matter in which way you use it. As long as you use it. If you try to break this rule, i.e. if your try to declare a object, but not use it, the zig compiler will not compile your Zig source code, and it will issue a error message warning that you have unused objects in your code.\nLet’s demonstrate this with an example. In the source code below, we declare a constant object called age. If you try to compile a simple Zig program with this line of code below, the compiler will return an error as demonstrated below:\n\nconst age = 15;\n\nt.zig:4:11: error: unused local constant\n const age = 15;\n ^~~\nEverytime you declare a new object in Zig, you have two choices:\n\nyou either use the value of this object;\nor 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 this object to a special character in Zig, which is the underscore (_). When you assign an object to a underscore, like in the example below, the zig compiler will automatically discard the value of this particular object.\nYou can see in the example below that, this time, the compiler did not complain about any “unused constant”, and successfully compiled our source code.\n\n// It compiles!\nconst age = 15;\n_ = age;\n\nNow, remember, everytime you assign a particular object to the underscore, this object is essentially destroyed. It is discarded by the compiler. This means that you can no longer use this object further in your code. It doesn’t exist anymore.\nSo if you try to use the constant age in the example below, after we discarded it, you will get a loud error message from the compiler (talking about a “pointless discard”) warning you about this mistake.\n\n// It does not compile.\nconst age = 15;\n_ = age;\n// Using a discarded value!\nstd.debug.print(\"{d}\\n\", .{age + 2});\n\nt.zig:7:5: error: pointless discard\n of local constant\nThis same rule applies to variable objects. Every variable object must also be used in some way. And if you assign a variable object to the underscore, this object also get’s discarded, and you can no longer use this object.\n\n\n1.4.4 You must mutate every variable objects\nEvery variable object that you create in your source code must be mutated at some point. In other words, if you declare an object as a variable object, with the keyword var, and you do not change the value of this object at some point in the future, the zig compiler will detect this, and it will raise an error warning you about this mistake.\nThe concept behind this is that every object you create in Zig should be preferably a constant object, unless you really need an object whose value will change during the execution of your program.\nSo, if I try to declare a variable object such as where_i_live below, and I do not change the value of this object in some way, the zig compiler raises an error message with the phrase “variable is never mutated”.\n\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n\nt.zig:7:5: error: local variable is never mutated\nt.zig:7:5: note: consider using 'const'",
"crumbs": [
"1Introducing Zig"
]
From b9d4b94598b9645e161f2ace6d73cfc0026d8752 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 9 Feb 2025 10:58:34 -0300
Subject: [PATCH 059/151] Recompile book with changes
---
.../01-zig-weird/execute-results/html.json | 4 ++--
docs/Chapters/01-zig-weird.html | 14 +++++++-------
docs/search.json | 4 ++--
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/_freeze/Chapters/01-zig-weird/execute-results/html.json b/_freeze/Chapters/01-zig-weird/execute-results/html.json
index 3f48d096..1238c4cd 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": "5041be02baff66bca29c690e3dfbfd7c",
+ "hash": "8ffa4a01e784b723e1aaf1806367c53c",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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\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\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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\n\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\n\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \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 does 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 can you 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://ziglings.org)[^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 specify 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 compile successfully. 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\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\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 successfully.\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\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;\n1. 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 a 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 successfully compiled our source code.\n\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\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\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\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 objects 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\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\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 common 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 {#sec-select-array-elem}\n\nOne very common 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\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\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\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\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\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\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\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\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 accompanied 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\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\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\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\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\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\n### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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\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\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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\n\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\n\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\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\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\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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. As an example, the `string_object` object below is 43 bytes long:\n\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\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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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\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.write(\n \"Bytes that represents the string object: \"\n );\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\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\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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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\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\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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\n\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\n\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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 portable 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\nIn 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOS. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \n\n\n### Compiling the entire project {#sec-compile-project}\n\nJust as I described in @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 does 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 later 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 can you 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 to 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 documentation.\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://ziglings.org)[^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 (on 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 specify 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 compile successfully. 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\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\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 successfully.\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\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;\n1. 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 a 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 successfully compiled our source code.\n\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\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\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\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 objects 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\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\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 common 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 {#sec-select-array-elem}\n\nOne very common 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\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\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\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\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\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\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\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\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 accompanied 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\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\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\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\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\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\n### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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\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\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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\n\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\n\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\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\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\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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. As an example, the `string_object` object below is 43 bytes long:\n\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\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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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\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.write(\n \"Bytes that represents the string object: \"\n );\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\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 1bd1fbd3..fed40272 100644
--- a/docs/Chapters/01-zig-weird.html
+++ b/docs/Chapters/01-zig-weird.html
@@ -469,14 +469,14 @@
1.2.5 Compile and execute at the same time
-
On 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.
+
In 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.
But 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.
zig run src/main.zig
Hello, world!
1.2.6 Important note for Windows users
-
First of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOs. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.
+
First of all, this is a Windows-specific thing, and, therefore, does not apply to other operating systems, such as Linux and macOS. In summary, if you have a piece of Zig code that includes some global variables whose initialization rely on runtime resources, then, you might have some troubles while trying to compile this Zig code on Windows.
An example of that is accessing the stdout (i.e. the standard output of your system), which is usually done in Zig by using the expression std.io.getStdOut(). Thus, if you use this expression to instantiate a global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows, with a “unable to evaluate comptime expression” error message.
This failure in the compilation process happens because all global variables in Zig are initialized at compile-time. However, operations like accessing the stdout (or opening a file) on Windows depends on resources that are available only at runtime (you will learn more about compile-time versus runtime at Section 3.1.1).
For example, if you try to compile this code example on Windows, you will likely get the error message exposed below:
@@ -508,12 +508,12 @@
1.2.7 Compiling the entire project
-
Just 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”.
+
Just as I described in 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”.
In 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.
In 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.
So, 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 does 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.
zig build
-
After 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.
+
After 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 later in this book.
In the example below, I’m executing the binary executable named hello_world that was generated by the compiler after the zig build command.
All these assets are available on GitHub, and this is great, because we can use the GitHub search bar in our advantage, to find Zig code that fits our description. For example, you can always include lang:Zig in the GitHub search bar when you are searching for a particular pattern. This will limit the search to only Zig modules.
-
Also, a great alternative is to consult online resources and documentations. Here is a quick list of resources that I personally use from time to time to learn more about the language each day:
+
All these assets are available on GitHub, and this is great, because we can use the GitHub search bar to our advantage, to find Zig code that fits our description. For example, you can always include lang:Zig in the GitHub search bar when you are searching for a particular pattern. This will limit the search to only Zig modules.
+
Also, a great alternative is to consult online resources and documentation. Here is a quick list of resources that I personally use from time to time to learn more about the language each day:
Another great strategy to learn Zig, or honestly, to learn any language you want, is to practice it by solving exercises. For example, there is a famous repository in the Zig community called Ziglings18 , which contains more than 100 small exercises that you can solve. It is a repository of tiny programs written in Zig that are currently broken, and your responsibility is to fix these programs, and make them work again.
-
A famous tech YouTuber known as The Primeagen also posted some videos (at YouTube) where he solves these exercises from Ziglings. The first video is named “Trying Zig Part 1”19.
+
A famous tech YouTuber known as The Primeagen also posted some videos (on YouTube) where he solves these exercises from Ziglings. The first video is named “Trying Zig Part 1”19.
Another great alternative, is to solve the Advent of Code exercises20. There are people that already took the time to learn and solve the exercises, and they posted their solutions on GitHub as well, so, in case you need some resource to compare while solving the exercises, you can look at these two repositories:
The other side of the spectrum are objects whose values are not known at compile time. Function arguments are a classic example of this. Because the value of each function argument depends on the value that you assign to this particular argument, when you call the function.
For example, the function input_length() contains an argument named input, which is an array of constant u8 integers ([]const u8). It is impossible to know the value of this particular argument at compile time. And it also is impossible to know the size/length of this particular argument. Because it is an array that do not have a fixed size specified explicitly in the argument type annotation.
-
So, we know that this input argument will be an array of u8 integers. But we do not know at compile-time, its value, and neither his size. This information is known only at runtime, which is the period of time when you program is executed. As a consequence, the value of the expression input.len is also known only at runtime. This is an intrinsic characteristic of any function. Just remember that the value of function arguments is usually not “compile-time known”.
+
So, we know that this input argument will be an array of u8 integers. But we do not know at compile-time, its value, and neither its size. This information is known only at runtime, which is the period of time when you program is executed. As a consequence, the value of the expression input.len is also known only at runtime. This is an intrinsic characteristic of any function. Just remember that the value of function arguments is usually not “compile-time known”.
However, as I mentioned earlier, what really matters to the compiler is to know the size of the object at compile-time, and not necessarily its value. So, although we don’t know the value of the object n, which is the result of the expression input.len, at compile-time, we do know its size. Because the expression input.len always returns a value of type usize, and the type usize has a known fixed size.
@@ -391,7 +391,7 @@
<
3.1.3 Stack vs Heap
-
If you are familiar with system’s programming, or just low-level programming in general, you probably have heard of the “duel” between Stack vs Heap. These are two different types of memory, or different memory spaces, which are both available in Zig.
+
If you are familiar with systems programming, or just low-level programming in general, you probably have heard of the “duel” between Stack vs Heap. These are two different types of memory, or different memory spaces, which are both available in Zig.
These two types of memory don’t actually duel with each other. This is a common mistake that beginners have, when seeing “x vs y” styles of tabloid headlines. These two types of memory are actually complementary to each other. So, in almost every Zig program that you ever write, you will likely use a combination of both. I will describe each memory space in detail over the next sections. But for now, I just want to stablish the main difference between these two types of memory.
In essence, the stack memory is normally used to store values whose length is fixed and known at compile time. In contrast, the heap memory is a dynamic type of memory space, meaning that, it is used to store values whose length might grow during the execution (runtime) of your program (Chen and Guo 2022).
Lengths that grow during runtime are intrinsically associated with “runtime known” type of values. In other words, if you have an object whose length might grow during runtime, then, the length of this object becomes not known at compile time. If the length is not known at compile-time, the value of this object also becomes not known at compile-time. These types of objects should be stored in the heap memory space, which is a dynamic memory space, which can grow or shrink to fit the size of your objects.
@@ -429,7 +429,7 @@
Local objects that are stored in the stack space of a function are automatically freed/destroyed at the end of the function scope.
-
This same logic applies to any other special structure in Zig that have its own scope by surrounding it with curly braces ({}). For loops, while loops, if else statements, etc. For example, if you declare any local object in the scope of a for loop, this local object is accessible only within the scope of this particular for loop. Because once the scope of this for loop ends, the space in the stack reserved for this for loop is freed. The example below demonstrates this idea.
+
This same logic applies to any other special structure in Zig that has its own scope by surrounding it with curly braces ({}). For loops, while loops, if else statements, etc. For example, if you declare any local object in the scope of a for loop, this local object is accessible only within the scope of this particular for loop. Because once the scope of this for loop ends, the space in the stack reserved for this for loop is freed. The example below demonstrates this idea.
// This does not compile successfully!const a = [_]u8{0, 1, 2, 3, 4};
@@ -460,7 +460,7 @@
const r = add(5, 27); _ = r;}
-
This “invalid pointer to stack variable” problem is very known across many programming language communities. If you try to do the same thing, for example, in a C or C++ program (i.e. returning an address to a local object stored in the stack), you would also get undefined behaviour in the program.
+
This “invalid pointer to stack variable” problem is well known across many programming language communities. If you try to do the same thing, for example, in a C or C++ program (i.e. returning an address to a local object stored in the stack), you would also get undefined behaviour in the program.
@@ -474,13 +474,13 @@
If a local object in your function is stored in the stack, you should never return a pointer to this local object from the function. Because this pointer will always become undefined after the function returns, since the stack space of the function is destroyed at the end of its scope.
-
But what if you really need to use this local object in some way after your function returns? How can you do this? The answer is: “in the same you would do if this was a C or C++ program. By returning an address to an object stored in the heap”. The heap memory has a much more flexible lifecycle, and allows you to get a valid pointer to a local object of a function that already returned from its scope.
+
But what if you really need to use this local object in some way after your function returns? How can you do this? The answer is: “in the same way you would do if this were a C or C++ program. By returning an address to an object stored in the heap”. The heap memory has a much more flexible lifecycle, and allows you to get a valid pointer to a local object of a function that already returned from its scope.
3.1.5 Heap
One important limitation of the stack, is that, only objects whose length/size is known at compile-time can be stored in it. In contrast, the heap is a much more dynamic (and flexible) type of memory. It is the perfect type of memory to use for objects whose size/length might grow during the execution of your program.
Virtually any application that behaves as a server is a classic use case of the heap. A HTTP server, a SSH server, a DNS server, a LSP server, … any type of server. In summary, a server is a type of application that runs for long periods of time, and that serves (or “deals with”) any incoming request that reaches this particular server.
-
The heap is a good choice for this type of system, mainly because the server does not know upfront how many requests it will receive from users, while it is active. It could be one single request, or, 5 thousand requests, or, it could also be zero requests. The server needs to have the ability to allocate and manage its memory according to how many requests it receives.
+
The heap is a good choice for this type of system, mainly because the server does not know upfront how many requests it will receive from users, while it is active. It could be a single request, 5 thousand requests, or even zero requests. The server needs to have the ability to allocate and manage its memory according to how many requests it receives.
Another key difference between the stack and the heap, is that the heap is a type of memory that you, the programmer, have complete control over. This makes the heap a more flexible type of memory, but it also makes it harder to work with. Because you, the programmer, is responsible for managing everything related to it. Including where the memory is allocated, how much memory is allocated, and where this memory is freed.
Unlike stack memory, heap memory is allocated explicitly by programmers and it won’t be deallocated until it is explicitly freed (Chen and Guo 2022).
@@ -509,7 +509,7 @@
Allocating memory on the stack is generally faster than allocating it on the heap. But this better performance comes with many restrictions. We have already discussed many of these restrictions of the stack at Section 3.1.4. But there is one more important limitation that I want to talk about, which is the size of the stack itself.
The stack is limited in size. This size varies from computer to computer, and it depends on a lot of things (the computer architecture, the operating system, etc.). Nevertheless, this size is usually not that big. This is why we normally use the stack to store only temporary and small objects in memory.
In essence, if you try to make an allocation on the stack, that is so big that exceeds the stack size limit, a stack overflow happens, and your program just crashes as a result of that. In other words, a stack overflow happens when you attempt to use more space than is available on the stack.
-
This type of problem is very similar to a buffer overflow, i.e. you are trying to use more space than is available in the “buffer object”. However, a stack overflow always cause your program to crash, while a buffer overflow not always cause your program to crash (although it often does).
-
You can see an example of a stack overflow in the example below. We are trying to allocate a very big array of u64 values on the stack. You can see below that this program does not run succesfully, because it crashed with a “segmentation fault” error message.
+
This type of problem is very similar to a buffer overflow, i.e. you are trying to use more space than is available in the “buffer object”. However, a stack overflow always causes your program to crash, while a buffer overflow does not always cause your program to crash (although it often does).
+
You can see an example of a stack overflow in the example below. We are trying to allocate a very big array of u64 values on the stack. You can see below that this program does not run successfully, because it crashed with a “segmentation fault” error message.
var very_big_alloc: [1000 * 1000 * 24]u64 = undefined;@memset(very_big_alloc[0..], 0);
@@ -533,7 +533,7 @@
This is a known problem, especially in C++. Because in C++, there are some operators that do allocate memory behind the scene, and there is no way for you to know that, until you actually read the source code of these operators, and find the memory allocation calls. Many programmers find this behaviour annoying and hard to keep track of.
But, in Zig, if a function, an operator, or anything from the standard library needs to allocate some memory during its execution, then, this function/operator needs to receive (as input) an allocator provided by the user, to actually be able to allocate the memory it needs.
This creates a clear distinction between functions that “do not” from those that “actually do” allocate memory. Just look at the arguments of this function. If a function, or operator, has an allocator object as one of its inputs/arguments, then, you know for sure that this function/operator will allocate some memory during its execution.
-
An example is the allocPrint() function from the Zig Standard Library. With this function, you can write a new string using format specifiers. So, this function is, for example, very similar to the function sprintf() in C. In order to write such new string, the allocPrint() function needs to allocate some memory to store the output string.
+
An example is the allocPrint() function from the Zig Standard Library. With this function, you can write a new string using format specifiers. So, this function is, for example, very similar to the function sprintf() in C. In order to write such a new string, the allocPrint() function needs to allocate some memory to store the output string.
That is why, the first argument of this function is an allocator object that you, the user/programmer, gives as input to the function. In the example below, I am using the GeneralPurposeAllocator() as my allocator object. But I could easily use any other type of allocator object from the Zig Standard Library.
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@@ -558,14 +558,14 @@
3.3.2 Why you need an allocator?
-
As we described at Section 3.1.4, everytime you make a function call in Zig, a space in the stack is reserved for this function call. But the stack has a key limitation which is: every object stored in the stack have a known fixed length.
+
As we described at Section 3.1.4, everytime you make a function call in Zig, a space in the stack is reserved for this function call. But the stack has a key limitation which is: every object stored in the stack has a known fixed length.
But in reality, there are two very common instances where this “fixed length limitation” of the stack is a deal braker:
the objects that you create inside your function might grow in size during the execution of the function.
sometimes, it is impossible to know upfront how many inputs you will receive, or how big this input will be.
Also, there is another instance where you might want to use an allocator, which is when you want to write a function that returns a pointer to a local object. As I described at Section 3.1.4, you cannot do that if this local object is stored in the stack. However, if this object is stored in the heap, then, you can return a pointer to this object at the end of the function. Because you (the programmer) control the lifetime of any heap memory that you allocate. You decide when this memory gets destroyed/freed.
-
These are common situations where the stack is not good for. That is why you need a different memory management strategy to store these objects inside your function. You need to use a memory type that can grow together with your objects, or that you can control the lifetime of this memory. The heap fits this description.
+
These are common situations for which the stack is not good. That is why you need a different memory management strategy to store these objects inside your function. You need to use a memory type that can grow together with your objects, or that you can control the lifetime of this memory. The heap fits this description.
Allocating memory on the heap is commonly known as dynamic memory management. As the objects you create grow in size during the execution of your program, you grow the amount of memory you have by allocating more memory in the heap to store these objects. And you do that in Zig, by using an allocator object.
That is the essence of print debugging. Is to use print expressions to see the values that are being generated by your program, and, as a result, get a much better understanding of how your program is behaving.
Many programmers often resort to the print functions in Zig, such as the stdout.print(), or, the std.debug.print(), to get a better understanding of their programs. This is a known and old strategy that is very simple and effective, and it is better known within the programming community as print debugging. In Zig, you can print information to the stdout or stderr streams of your system.
Let’s begin with stdout. First, you need to get access to the stdout, by calling the getStdOut() method, from the Zig Standard Library. This method returns a file descriptor object, and, through this object you can read/write to the stdout. I recommend you to check out all methods available in this object, by checking the page in the Zig Standard Library Official Reference for the type File1.
-
For our purpose here, which is to write something to the stdout, especially to debug our program, I recommend you to use the writer() method, which gives your a writer object. This writer object offers some helper methods to write stuff into the file descriptor object that represents the stdout stream. In special, the print() method.
+
For our purpose here, which is to write something to the stdout, especially to debug our program, I recommend you to use the writer() method, which gives you a writer object. This writer object offers some helper methods to write stuff into the file descriptor object that represents the stdout stream. In special, the print() method.
The print() method from this writer object is a “print formatter” type of a function. In other words, this method works exactly like the printf() function from C, or, like println!() from Rust. In the first argument of the function, you specify a template string, and, in the second argument, you provide a list of values (or objects) that you want to insert into your template message.
Ideally, the template string in the first argument should contain some format specifier. Each format specifier is matched to a value (or object) that you have listed in the second argument. So, if you provided 5 different objects in the second argument, then, the template string should contain 5 format specifiers, one for each object provided.
Each format specifier is represented by a single letter, and you provide this format specifier inside a pair of curly braces. So, if you want to format your object using the string specifier (s), then, you can insert the text {s} in your template string. Here is a quick list of the most used format specifiers:
@@ -345,7 +345,7 @@
Result: 50
-
Is important to emphasize that, the stdout.print() method, as you would expect, print your template string into the stdout stream of your system. However, you can also print your template string into the stderr stream if your prefer. All you need to do, is to replace the stdout.print() call with the function std.debug.print(). Like this:
+
It is important to emphasize that, the stdout.print() method, as you would expect, prints your template string into the stdout stream of your system. However, you can also print your template string into the stderr stream if your prefer. All you need to do, is to replace the stdout.print() call with the function std.debug.print(). Like this:
Although print debugging being a valid and very useful strategy, most programmers prefer to use a debugger to debug their programs. Since Zig is a low-level language, you can use either GDB (GNU Debugger), or LLDB (LLVM Project Debugger) as your debugger.
+
Although print debugging is a valid and very useful strategy, most programmers prefer to use a debugger to debug their programs. Since Zig is a low-level language, you can use either GDB (GNU Debugger), or LLDB (LLVM Project Debugger) as your debugger.
Both debuggers can work with Zig code, and it’s a matter of taste here. You choose the debugger of your preference, and you work with it. In this book, I will use LLDB as my debugger on the examples.
5.2.1 Compile your source code in debug mode
@@ -424,7 +424,7 @@
<
I can start navigating through the code, and checking the objects that are being generated. If you are not familiar with the commands available in LLDB, I recommend you to read the official documentation of the project3. You can also look for cheat sheets, which quickly describes all commands available for you4.
Currently, we are in the first line at the main() function. In this line, we create the n object, by executing the add_and_increment() function. To execute the current line of code, and go to the next line, we can run the n LLDB command. Let’s execute this command.
After we executed this line, we can also look at the value stored inside this n object by using the p LLDB command. The syntax for this command is p <name-of-object>.
-
If we take a look at the value stored in the n object (p n), notice that it stores the hexadecimal value 0x06, which is the number 6 in decimal. We can also see that, this value have a type unsigned char, which is an unsigned 8-bit integer. We have talked already about this at Section 1.8, that u8 integers in Zig are equivalent to the C data type unsigned char.
+
If we take a look at the value stored in the n object (p n), notice that it stores the hexadecimal value 0x06, which is the number 6 in decimal. We can also see that this value has a type of unsigned char, which is an unsigned 8-bit integer. We have talked already about this at Section 1.8, that u8 integers in Zig are equivalent to the C data type unsigned char.
Therefore, we can use defer to declare an expression that is going to be executed when your code exits the current scope. Some programmers like to interpret the phrase “exit of the current scope” as “the end of the current scope”. But this interpretation might not be entirely correct, depending on what you consider as “the end of the current scope”.
-
I mean, what do you consider as the end of the current scope? Is it the closing curly bracket (}) of the scope? Is it when the last expression in the function get’s executed? Is it when the function returns to the previous scope? Etc. For example, it would not be correct to interpret the “exit of the current scope” as the closing curly bracket of the scope. Because the function might exit from an earlier position than this closing curly bracket (e.g. an error value was generated at a previous line inside the function; the function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.
+
I mean, what do you consider as the end of the current scope? Is it the closing curly bracket (}) of the scope? Is it when the last expression in the function gets executed? Is it when the function returns to the previous scope? Etc. For example, it would not be correct to interpret the “exit of the current scope” as the closing curly bracket of the scope. Because the function might exit from an earlier position than this closing curly bracket (e.g. an error value was generated at a previous line inside the function; the function reached an earlier return statement; etc.). Anyway, just be careful with this interpretation.
Now, if you remember of what we have discussed in Section 1.7, there are multiple structures in the language that create their own separate scopes. For/while loops, if/else statements, functions, normal blocks, etc. This also affects the interpretation of defer. For example, if you use defer inside a for loop, then, the given expression will be executed everytime this specific for loop exits its own scope.
Before we continue, is worth emphasizing that the defer keyword is an “unconditional defer”. Which means that the given expression will be executed no matter how the code exits the current scope. For example, your code might exit the current scope because of an error value being generated, or, because of a return statement, or, a break statement, etc.
@@ -809,7 +809,7 @@
Sometimes you don’t need to care about the state of your struct object. Sometimes, you just need to instantiate and use the objects, without altering their state. You can notice that when you have methods inside your struct declaration that might use the values that are present in the data members, but they do not alter the values in these data members of the struct in anyway.
The Vec3 struct that was presented in Section 2.3.3 is an example of that. This struct have a single method named distance(), and this method does use the values present in all three data members of the struct (x, y and z). But at the same time, this method does not change the values of these data members at any point.
As a result of that, when we create Vec3 objects we usually create them as constant objects, like the v1 and v2 objects presented in Section 2.3.3. We can create them as variable objects with the var keyword, if we want to. But because the methods of this Vec3 struct do not change the state of the objects in any point, it’s unnecessary to mark them as variable objects.
-
But why? Why am I talking about this here? It’s because the self argument in the methods is affected depending on whether the methods present in a struct change or don’t change the state of the object itself. More specifically, when you have a method in a struct that changes the state of the object (i.e. change the value of a data member), the self argument in this method must be annotated in a different manner.
+
But why? Why am I talking about this here? It’s because the self argument in the methods are affected depending on whether the methods present in a struct change or don’t change the state of the object itself. More specifically, when you have a method in a struct that changes the state of the object (i.e. changes the value of a data member), the self argument in this method must be annotated in a different manner.
As I described in Section 2.3.3, the self argument in methods of a struct is the argument that receives as input the object from which the method was called from. We usually annotate this argument in the methods by writing self, followed by the colon character (:), and the data type of the struct to which the method belongs to (e.g. User, Vec3, etc.).
If we take the Vec3 struct that we defined in the previous section as an example, we can see in the distance() method that this self argument is annotated as self: Vec3. Because the state of the Vec3 object is never altered by this method.
But what if we do have a method that alters the state of the object, by altering the values of its data members, how should we annotate self in this instance? The answer is: “we should annotate self as a pointer of x, instead of just x”. In other words, you should annotate self as self: *x, instead of annotating it as self: x.
@@ -877,7 +877,7 @@
2.4 Type inference
Zig is a strongly typed language. But, there are some situations where you don’t have to explicitly write the type of every single object in your source code, as you would expect from a traditional strongly typed language, such as C and C++.
In some situations, the zig compiler can use type inference to solve the data types for you, easing some of the burden that you carry as a developer. The most common way this happens is through function arguments that receive struct objects as input.
-
In general, type inference in Zig is done by using the dot character (.). Everytime you see a dot character written before a struct literal, or before an enum value, or something like that, you know that this dot character is playing a special party in this place. More specifically, it is telling the zig compiler something along the lines of: “Hey! Can you infer the type of this value for me? Please!”. In other words, this dot character is playing a similar role as the auto keyword in C++.
+
In general, type inference in Zig is done by using the dot character (.). Everytime you see a dot character written before a struct literal, or before an enum value, or something like that, you know that this dot character is playing a special part in this place. More specifically, it is telling the zig compiler something along the lines of: “Hey! Can you infer the type of this value for me? Please!”. In other words, this dot character is playing a similar role as the auto keyword in C++.
I gave you some examples of this in Section 2.3.1, where we used anonymous struct literals. Anonymous struct literals are, struct literals that use type inference to infer the exact type of this particular struct literal. This type inference is done by looking for some minimal hint of the correct data type to be used. You could say that the zig compiler looks for any neighbouring type annotation that might tell it what the correct type would be.
Another common place where we use type inference in Zig is at switch statements (which we talked about in Section 2.1.2). I also gave some other examples of type inference in Section 2.1.2, where we were inferring the data types of enum values listed inside of switch statements (e.g. .DE). But as another example, take a look at this fence() function reproduced below, which comes from the atomic.zig module2 of the Zig Standard Library.
There are a lot of things in this function that we haven’t talked about yet, such as: what comptime means? inline? extern? Let’s just ignore all of these things, and focus solely on the switch statement that is inside this function.
@@ -927,8 +927,7 @@
try expect(@TypeOf(y) == u32);}
-
1/1 filecdc41797a754.test_0...OKAll 1 tests passed
- d.
This is the general way to perform type casting in Zig. But remember, @as() works only when casting is unambiguous and safe, and there are situations where these assumptions do not hold. For example, when casting an integer value into a float value, or vice-versa, it is not clear to the compiler how to perform this conversion safely.
This book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):
-
Calin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m)
+
Calin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m), Alexander (@alexwheezy)
diff --git a/docs/search.json b/docs/search.json
index 3c57d704..1999409e 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -84,7 +84,7 @@
"href": "index.html#acknowledgments",
"title": "Introduction to Zig",
"section": "Acknowledgments",
- "text": "Acknowledgments\nThis book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):\nCalin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m)",
+ "text": "Acknowledgments\nThis book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):\nCalin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m), Alexander (@alexwheezy)",
"crumbs": [
"Welcome"
]
From 9ea48cdba5768a585cf83adf2fdad249fdaa2a92 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 9 Feb 2025 12:05:24 -0300
Subject: [PATCH 064/151] Fix code example
---
Chapters/05-pointers.qmd | 15 +++++++++------
.../05-pointers/execute-results/html.json | 8 +++-----
docs/Chapters/05-pointers.html | 10 +++++-----
docs/search.json | 2 +-
4 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/Chapters/05-pointers.qmd b/Chapters/05-pointers.qmd
index 5658c4fc..3e2d8dc1 100644
--- a/Chapters/05-pointers.qmd
+++ b/Chapters/05-pointers.qmd
@@ -249,14 +249,17 @@ to the first element in the array `ar`. But then, I started to walk through the
the pointer with simple pointer arithmetic.
```{zig}
-#| eval: false
-const ar = [_]i32{1,2,3,4};
-var ptr = &ar;
-try stdout.print("{d}\n", .{ptr.*});
+#| eval: true
+#| auto_main: true
+#| build_type: "run"
+#| results: "hide"
+const ar = [_]i32{ 1, 2, 3, 4 };
+var ptr: [*]const i32 = &ar;
+try stdout.print("{d}\n", .{ptr[0]});
ptr += 1;
-try stdout.print("{d}\n", .{ptr.*});
+try stdout.print("{d}\n", .{ptr[0]});
ptr += 1;
-try stdout.print("{d}\n", .{ptr.*});
+try stdout.print("{d}\n", .{ptr[0]});
```
```
diff --git a/_freeze/Chapters/05-pointers/execute-results/html.json b/_freeze/Chapters/05-pointers/execute-results/html.json
index 0cdd4021..bce4789c 100644
--- a/_freeze/Chapters/05-pointers/execute-results/html.json
+++ b/_freeze/Chapters/05-pointers/execute-results/html.json
@@ -1,11 +1,9 @@
{
- "hash": "9520ae65eeada418ccd4d4ca5f8b6f7a",
+ "hash": "8fdb1694a88afd9b81c337b29ec84c86",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n# Pointers and Optionals {#sec-pointer}\n\nOn our next project we are going to build a HTTP server from scratch.\nBut in order to do that, we need to learn more about pointers and how they work in Zig.\nPointers in Zig are similar to pointers in C. But they come with some extra advantages in Zig.\n\nA pointer is an object that contains a memory address. This memory address is the address where\na particular value is stored in memory. It can be any value. Most of the times,\nit is a value that comes from another object (or variable) present in our code.\n\nIn the example below, I'm creating two objects (`number` and `pointer`).\nThe `pointer` object contains the memory address where the value of the `number` object\n(the number 5) is stored. So, that is a pointer in a nutshell. It is a memory\naddress that points to a particular existing value in the memory. You could\nalso say, that, the `pointer` object points to the memory address where the `number` object is\nstored.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst number: u8 = 5;\nconst pointer = &number;\n_ = pointer;\n```\n:::\n\n\n\n\nWe create a pointer object in Zig by using the `&` operator. When you put this operator\nbefore the name of an existing object, you get the memory address of this object as result.\nWhen you store this memory address inside a new object, this new object becomes a pointer object.\nBecause it stores a memory address.\n\nPeople mostly use pointers as an alternative way to access a particular value.\nFor example, I can use the `pointer` object to access the value stored by\nthe `number` object. This operation of accessing the value that the\npointer \"points to\" is normally called of *dereferencing the pointer*.\nWe can dereference a pointer in Zig by using the `*` method of the pointer object. Like in the example\nbelow, where we take the number 5 pointed by the `pointer` object,\nand double it.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst number: u8 = 5;\nconst pointer = &number;\nconst doubled = 2 * pointer.*;\nstd.debug.print(\"{d}\\n\", .{doubled});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n10\n```\n\n\n:::\n:::\n\n\n\n\n\nThis syntax to dereference the pointer is nice. Because we can easily chain it with\nmethods of the value pointed by the pointer. We can use the `User` struct that we have\ncreated at @sec-structs-and-oop as an example. If you comeback to that section,\nyou will see that this struct have a method named `print_name()`.\n\nSo, for example, if we have an user object, and a pointer that points to this user object,\nwe can use the pointer to access this user object, and, at the same time, call the method `print_name()`\non it, by chaining the dereference method (`*`) with the `print_name()` method. Like in the\nexample below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst u = User.init(1, \"pedro\", \"email@gmail.com\");\nconst pointer = &u;\ntry pointer.*.print_name();\n```\n:::\n\n\n\n\n```\npedro\n```\n\nWe can also use pointers to effectively alter the value of an object.\nFor example, I could use the `pointer` object to set\nthe value of the object `number` to 6, like in the example below.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar number: u8 = 5;\nconst pointer = &number;\npointer.* = 6;\ntry stdout.print(\"{d}\\n\", .{number});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n6\n```\n\n\n:::\n:::\n\n\n\n\n\nTherefore, as I mentioned earlier, people use pointers as an alternative way to access a particular value.\nAnd they use it especially when they do not want to \"move\" these values around. There are situations where,\nyou want to access a particular value in a different scope (i.e. a different location) of your code,\nbut you do not want to \"move\" this value to this new scope (or location) that you are in.\n\nThis matters especially if this value is big in size. Because if it is, then,\nmoving this value becomes an expensive operation to do.\nThe computer will have to spend a considerable amount of time\ncopying this value to this new location.\n\nTherefore, many programmers prefer to avoid this heavy operation of copying the value\nto the new location, by accessing this value through pointers.\nWe are going to talk more about this \"moving operation\" over the next sections.\nFor now, just keep in mind that avoiding this \"move operation\" is\none of main reasons why pointers are used in programming languages.\n\n\n\n\n\n## Constant objects vs variable objects {#sec-pointer-var}\n\nYou can have a pointer that points to a constant object, or, a pointer that points to a variable object.\nBut regardless of who this pointer is, a pointer **must always respect the characteristics of the object that it points to**.\nAs a consequence, if the pointer points to a constant object, then, you cannot use this pointer\nto change the value that it points to. Because it points to a value that is constant. As we discussed at @sec-assignments, you cannot\nchange a value that is constant.\n\nFor example, if I have a `number` object, which is constant, I cannot execute\nthe expression below where I'm trying to change the value of `number` to 6 through\nthe `pointer` object. As demonstrated below, when you try to do something\nlike that, you get a compile time error:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst number = 5;\nconst pointer = &number;\npointer.* = 6;\n```\n:::\n\n\n\n\n```\np.zig:6:12: error: cannot assign to constant\n pointer.* = 6;\n```\n\nIf I change the `number` object to be a variable object, by introducing the `var` keyword,\nthen, I can successfully change the value of this object through a pointer, as demonstrated below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar number: u8 = 5;\nconst pointer = &number;\npointer.* = 6;\ntry stdout.print(\"{d}\\n\", .{number});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n6\n```\n\n\n:::\n:::\n\n\n\n\nYou can see this relationship between \"constant versus variable\" on the data type of\nyour pointer object. In other words, the data type of a pointer object already gives you\nsome clues about whether the value that it points to is constant or not.\n\nWhen a pointer object points to a constant value, then, this pointer have a data type `*const T`,\nwhich means \"a pointer to a constant value of type `T`\".\nIn contrast, if the pointer points to a variable value, then, the type of the pointer is usually `*T`, which is\nsimply \"a pointer to a value of type `T`\".\nHence, whenever you see a pointer object whose data type is in the format `*const T`, then,\nyou know that you cannot use this pointer to change the value that it points to.\nBecause this pointer points to a constant value of type `T`.\n\n\nWe have talked about the value pointed by the pointer being constant or not,\nand the consequences that arises from it. But, what about the pointer object itself? I mean, what happens\nif the pointer object itself is constant or not? Think about it.\nWe can have a constant pointer that points to a constant value.\nBut we can also have a variable pointer that points to a constant value. And vice-versa.\n\nUntil this point, the `pointer` object was always constant,\nbut what this means for us? What is the consequence of the\n`pointer` object being constant? The consequence is that\nwe cannot change the pointer object, because it is constant. We can use the\npointer object in multiple ways, but we cannot change the\nmemory address that is inside this pointer object.\n\nHowever, if we mark the `pointer` object as a variable object,\nthen, we can change the memory address pointed by this `pointer` object.\nThe example below demonstrates that. Notice that the object pointed\nby the `pointer` object changes from `c1` to `c2`.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst c1: u8 = 5;\nconst c2: u8 = 6;\nvar pointer = &c1;\ntry stdout.print(\"{d}\\n\", .{pointer.*});\npointer = &c2;\ntry stdout.print(\"{d}\\n\", .{pointer.*});\n```\n:::\n\n\n\n\n```\n5\n6\n```\n\nThus, by setting the `pointer` object to a `var` or `const` object,\nyou specify if the memory address contained in this pointer object can change or not\nin your program. On the other side, you can change the value pointed by the pointer,\nif, and only if this value is stored in a variable object. If this value\nis in a constant object, then, you cannot change this value through a pointer.\n\n\n## Types of pointer\n\nIn Zig, there are two types of pointers [@zigdocs], which are:\n\n- single-item pointer (`*`);\n- many-item pointer (`[*]`);\n\n\nSingle-item pointer objects are objects whose data types are in the format `*T`.\nSo, for example, if an object have a data type `*u32`, it means that, this\nobject contains a single-item pointer that points to an unsigned 32-bit integer value.\nAs another example, if an object have type `*User`, then, it contains\na single-item pointer to an `User` value.\n\nIn contrast, many-item pointers are objects whose data types are in the format `[*]T`.\nNotice that the star symbol (`*`) is now inside a pair of brackets (`[]`). If the star\nsymbol is inside a pair of brackets, you know that this object is a many-item pointer.\n\nWhen you apply the `&` operator over an object, you will always get a single-item pointer.\nMany-item pointers are more of a \"internal type\" of the language, more closely\nrelated to slices. So, when you deliberately create a pointer with the `&` operator,\nyou always get a single-item pointer as result.\n\n\n\n## Pointer arithmetic\n\nPointer arithmetic is available in Zig, and they work the same way they work in C.\nWhen you have a pointer that points to an array, the pointer usually points to\nthe first element in the array, and you can use pointer arithmetic to\nadvance this pointer and access the other elements in the array.\n\n\nNotice in the example below, that initially, the `ptr` object was pointing\nto the first element in the array `ar`. But then, I started to walk through the array, by advancing\nthe pointer with simple pointer arithmetic.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ar = [_]i32{1,2,3,4};\nvar ptr = &ar;\ntry stdout.print(\"{d}\\n\", .{ptr.*});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr.*});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr.*});\n```\n:::\n\n\n\n\n```\n1\n2\n3\n```\n\nAlthough you can create a pointer to an array like that, and\nstart to walk through this array by using pointer arithmetic,\nin Zig, we prefer to use slices, which were presented at @sec-arrays.\n\nBehind the hood, slices already are pointers,\nand they also come with the `len` property, which indicates\nhow many elements are in the slice. This is good because the `zig` compiler\ncan use it to check for potential buffer overflows, and other problems like that.\n\nAlso, you don't need to use pointer arithmetic to walk through the elements\nof a slice. You can simply use the `slice[index]` syntax to directly access\nany element you want in the slice.\nAs I mentioned at @sec-arrays, you can get a slice from an array by using\na range selector inside brackets. In the example below, I'm creating\na slice (`sl`) that covers the entire `ar` array. I can access any\nelement of `ar` from this slice, and, the slice itself already is a pointer\nbehind the hood.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ar = [_]i32{1,2,3,4};\nconst sl = ar[0..ar.len];\n_ = sl;\n```\n:::\n\n\n\n\n\n## Optionals and Optional Pointers\n\nLet's talk about optionals and how they relate to pointers in Zig.\nBy default, objects in Zig are **non-nullable**. This means that, in Zig,\nyou can safely assume that any object in your source code is not null.\n\nThis is a powerful feature of Zig when you compare it to the developer experience in C.\nBecause in C, any object can be null at any point, and, as consequence, a pointer in C\nmight point to a null value. This is a common source of undefined behaviour in C.\nWhen programmers work with pointers in C, they have to constantly check if\ntheir pointers are pointing to null values or not.\n\nIf for some reason, your Zig code produces a null value somewhere, and, this null\nvalue ends up in an object that is non-nullable, a runtime error is always\nraised by your Zig program. Take the program below as an example.\nThe `zig` compiler can see the `null` value at compile time, and, as result,\nit raises a compile time error. But, if a `null` value is raised during\nruntime, a runtime error is also raised by the Zig program, with a\n\"attempt to use null value\" message.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar number: u8 = 5;\nnumber = null;\n```\n:::\n\n\n\n```\np5.zig:5:14: error: expected type 'u8',\n found '@TypeOf(null)'\n number = null;\n ^~~~\n```\n\n\nYou don't get this type of safety in C.\nIn C, you don't get warnings or errors about null values being produced in your program.\nIf for some reason, your code produces a null value in C, most of the times, you end up getting a segmentation fault error\nas result, which can mean many things.\nThat is why programmers have to constantly check for null values in C.\n\nPointers in Zig are also, by default, **non-nullable**. This is another amazing\nfeature in Zig. So, you can safely assume that any pointer that you create in\nyour Zig code is pointing to a non-null value.\nTherefore, you don't have this heavy work of checking if the pointers you create\nin Zig are pointing to a null value.\n\n\n### What are optionals?\n\nOk, we know now that all objects are non-nullable by default in Zig.\nBut what if we actually need to use an object that might receive a null value?\nHere is where optionals come in.\n\nAn optional object in Zig is an object that can be null.\nTo mark an object as optional, we use the `?` operator. When you put\nthis `?` operator right before the data type of an object, you transform\nthis data type into an optional data type, and the object becomes an optional object.\n\nTake the snippet below as an example. We are creating a new variable object\ncalled `num`. This object have the data type `?i32`, which means that,\nthis object contains either a signed 32-bit integer (`i32`), or, a null value.\nBoth alternatives are valid values to the `num` object.\nThat is why, I can actually change the value of this object to null, and,\nno errors are raised by the `zig` compiler, as demonstrated below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar num: ?i32 = 5;\nnum = null;\n```\n:::\n\n\n\n\n### Optional pointers\n\nYou can also mark a pointer object as an optional pointer, meaning that,\nthis object contains either a null value, or, a pointer that points to a value.\nWhen you mark a pointer as optional, the data type of this pointer object\nbecomes `?*const T` or `?*T`, depending if the value pointed by the pointer\nis a constant value or not. The `?` identifies the object as optional, while\nthe `*` identifies it as a pointer object.\n\nIn the example below, we are creating a variable object named `num`, and an\noptional pointer object named `ptr`. Notice that the data type of the object\n`ptr` indicates that it is either a null value, or a pointer to an `i32` value.\nAlso, notice that the pointer object (`ptr`) can be marked as optional, even if\nthe object `num` is not optional.\n\nWhat this code tells us is that, the `num` variable will never contain a null value.\nThis variable will always contain a valid `i32` value. But in contrast, the `ptr` object might contain either a null\nvalue, or, a pointer to an `i32` value.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar num: i32 = 5;\nvar ptr: ?*i32 = #\nptr = null;\nnum = 6;\n```\n:::\n\n\n\n\nBut what happens if we turn the table, and mark the `num` object as optional,\ninstead of the pointer object. If we do that, then, the pointer object is\nnot optional anymore. It would be a similar (although different) result. Because then, we would have\na pointer to an optional value. In other words, a pointer to a value that is either a\nnull value, or, a not-null value.\n\nIn the example below, we are recreating this idea. Now, the `ptr` object\nhave a data type of `*?i32`, instead of `?*i32`. Notice that the `*` symbol comes before of `?`\nthis time. So now, we have a pointer that points to a value that is either null\n, or, a signed 32-bit integer.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar num: ?i32 = 5;\n// ptr have type `*?i32`, instead of `?*i32`.\nconst ptr = #\n_ = ptr;\n```\n:::\n\n\n\n\n\n### Null handling in optionals {#sec-null-handling}\n\nWhen you have an optional object in your Zig code, you have to explicitly handle\nthe possibility of this object being null. It is like error-handling with `try` and `catch`.\nIn Zig you also have to handle null values like if they were a type of error.\n\nWe can do that, by using either:\n\n- an if statement, like you would do in C.\n- the `orelse` keyword.\n- unwrap the optional value with the `?` method.\n\nWhen you use an if statement, you use a pair of pipes\nto unwrap the optional value, and use this \"unwrapped object\"\ninside the if block.\nUsing the example below as a reference, if the object `num` is null,\nthen, the code inside the if statement is not executed. Otherwise,\nthe if statement will unwrap the object `num` into the `not_null_num`\nobject. This `not_null_num` object is guaranteed to be not null inside\nthe scope of the if statement.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst num: ?i32 = 5;\nif (num) |not_null_num| {\n try stdout.print(\"{d}\\n\", .{not_null_num});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n5\n```\n\n\n:::\n:::\n\n\n\n\nNow, the `orelse` keyword behaves like a binary operator. You connect two expressions with this keyword.\nOn the left side of `orelse`, you provide the expression that might result\nin a null value, and on the right side of `orelse`, you provide another expression\nthat will not result in a null value.\n\nThe idea behind the `orelse` keyword is: if the expression on the left side\nresult in a not-null value, then, this not-null value is used. However,\nif this expression on the left side result in a null value, then, the value\nof the expression on the right side is used instead.\n\nLooking at the example below, since the `x` object is currently null, the\n`orelse` decided to use the alternative value, which is the number 15.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst x: ?i32 = null;\nconst dbl = (x orelse 15) * 2;\ntry stdout.print(\"{d}\\n\", .{dbl});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n30\n```\n\n\n:::\n:::\n\n\n\n\nYou can use the if statement or the `orelse` keyword, when you want to\nsolve (or deal with) this null value. However, if there is no clear solution\nto this null value, and the most logic and sane path is to simply panic\nand raise a loud error in your program when this null value is encountered,\nyou can use the `?` method of your optional object.\n\nIn essence, when you use this `?` method, the optional object is unwrapped.\nIf a not-null value is found in the optional object, then, this not-null value is used.\nOtherwise, the `unreachable` keyword is used. You can read more about this\n[`unreacheable` keyword at the official documentation](https://ziglang.org/documentation/master/#unreachable)[^un-docs].\nBut in essence, when you build your Zig source code using the build modes `ReleaseSafe` or `Debug`, this\n`unreacheable` keyword causes the program to panic and raise an error during runtime,\nlike in the example below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn return_null(n: i32) ?i32 {\n if (n == 5) return null;\n return n;\n}\n\npub fn main() !void {\n const x: i32 = 5;\n const y: ?i32 = return_null(x);\n try stdout.print(\"{d}\\n\", .{y.?});\n}\n```\n:::\n\n\n\n\n```\nthread 12767 panic: attempt to use null value\np7.zig:12:34: 0x103419d in main (p7):\n try stdout.print(\"{d}\\n\", .{y.?});\n ^\n```\n\n\n[^un-docs]: .\n\n\n",
- "supporting": [
- "05-pointers_files"
- ],
+ "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n\n# Pointers and Optionals {#sec-pointer}\n\nOn our next project we are going to build a HTTP server from scratch.\nBut in order to do that, we need to learn more about pointers and how they work in Zig.\nPointers in Zig are similar to pointers in C. But they come with some extra advantages in Zig.\n\nA pointer is an object that contains a memory address. This memory address is the address where\na particular value is stored in memory. It can be any value. Most of the times,\nit is a value that comes from another object (or variable) present in our code.\n\nIn the example below, I'm creating two objects (`number` and `pointer`).\nThe `pointer` object contains the memory address where the value of the `number` object\n(the number 5) is stored. So, that is a pointer in a nutshell. It is a memory\naddress that points to a particular existing value in the memory. You could\nalso say, that, the `pointer` object points to the memory address where the `number` object is\nstored.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst number: u8 = 5;\nconst pointer = &number;\n_ = pointer;\n```\n:::\n\n\n\n\n\nWe create a pointer object in Zig by using the `&` operator. When you put this operator\nbefore the name of an existing object, you get the memory address of this object as result.\nWhen you store this memory address inside a new object, this new object becomes a pointer object.\nBecause it stores a memory address.\n\nPeople mostly use pointers as an alternative way to access a particular value.\nFor example, I can use the `pointer` object to access the value stored by\nthe `number` object. This operation of accessing the value that the\npointer \"points to\" is normally called of *dereferencing the pointer*.\nWe can dereference a pointer in Zig by using the `*` method of the pointer object. Like in the example\nbelow, where we take the number 5 pointed by the `pointer` object,\nand double it.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst number: u8 = 5;\nconst pointer = &number;\nconst doubled = 2 * pointer.*;\nstd.debug.print(\"{d}\\n\", .{doubled});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n10\n```\n\n\n:::\n:::\n\n\n\n\n\n\nThis syntax to dereference the pointer is nice. Because we can easily chain it with\nmethods of the value pointed by the pointer. We can use the `User` struct that we have\ncreated at @sec-structs-and-oop as an example. If you comeback to that section,\nyou will see that this struct have a method named `print_name()`.\n\nSo, for example, if we have an user object, and a pointer that points to this user object,\nwe can use the pointer to access this user object, and, at the same time, call the method `print_name()`\non it, by chaining the dereference method (`*`) with the `print_name()` method. Like in the\nexample below:\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst u = User.init(1, \"pedro\", \"email@gmail.com\");\nconst pointer = &u;\ntry pointer.*.print_name();\n```\n:::\n\n\n\n\n\n```\npedro\n```\n\nWe can also use pointers to effectively alter the value of an object.\nFor example, I could use the `pointer` object to set\nthe value of the object `number` to 6, like in the example below.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar number: u8 = 5;\nconst pointer = &number;\npointer.* = 6;\ntry stdout.print(\"{d}\\n\", .{number});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n6\n```\n\n\n:::\n:::\n\n\n\n\n\n\nTherefore, as I mentioned earlier, people use pointers as an alternative way to access a particular value.\nAnd they use it especially when they do not want to \"move\" these values around. There are situations where,\nyou want to access a particular value in a different scope (i.e. a different location) of your code,\nbut you do not want to \"move\" this value to this new scope (or location) that you are in.\n\nThis matters especially if this value is big in size. Because if it is, then,\nmoving this value becomes an expensive operation to do.\nThe computer will have to spend a considerable amount of time\ncopying this value to this new location.\n\nTherefore, many programmers prefer to avoid this heavy operation of copying the value\nto the new location, by accessing this value through pointers.\nWe are going to talk more about this \"moving operation\" over the next sections.\nFor now, just keep in mind that avoiding this \"move operation\" is\none of main reasons why pointers are used in programming languages.\n\n\n\n\n\n## Constant objects vs variable objects {#sec-pointer-var}\n\nYou can have a pointer that points to a constant object, or, a pointer that points to a variable object.\nBut regardless of who this pointer is, a pointer **must always respect the characteristics of the object that it points to**.\nAs a consequence, if the pointer points to a constant object, then, you cannot use this pointer\nto change the value that it points to. Because it points to a value that is constant. As we discussed at @sec-assignments, you cannot\nchange a value that is constant.\n\nFor example, if I have a `number` object, which is constant, I cannot execute\nthe expression below where I'm trying to change the value of `number` to 6 through\nthe `pointer` object. As demonstrated below, when you try to do something\nlike that, you get a compile time error:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst number = 5;\nconst pointer = &number;\npointer.* = 6;\n```\n:::\n\n\n\n\n\n```\np.zig:6:12: error: cannot assign to constant\n pointer.* = 6;\n```\n\nIf I change the `number` object to be a variable object, by introducing the `var` keyword,\nthen, I can successfully change the value of this object through a pointer, as demonstrated below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar number: u8 = 5;\nconst pointer = &number;\npointer.* = 6;\ntry stdout.print(\"{d}\\n\", .{number});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n6\n```\n\n\n:::\n:::\n\n\n\n\n\nYou can see this relationship between \"constant versus variable\" on the data type of\nyour pointer object. In other words, the data type of a pointer object already gives you\nsome clues about whether the value that it points to is constant or not.\n\nWhen a pointer object points to a constant value, then, this pointer have a data type `*const T`,\nwhich means \"a pointer to a constant value of type `T`\".\nIn contrast, if the pointer points to a variable value, then, the type of the pointer is usually `*T`, which is\nsimply \"a pointer to a value of type `T`\".\nHence, whenever you see a pointer object whose data type is in the format `*const T`, then,\nyou know that you cannot use this pointer to change the value that it points to.\nBecause this pointer points to a constant value of type `T`.\n\n\nWe have talked about the value pointed by the pointer being constant or not,\nand the consequences that arises from it. But, what about the pointer object itself? I mean, what happens\nif the pointer object itself is constant or not? Think about it.\nWe can have a constant pointer that points to a constant value.\nBut we can also have a variable pointer that points to a constant value. And vice-versa.\n\nUntil this point, the `pointer` object was always constant,\nbut what this means for us? What is the consequence of the\n`pointer` object being constant? The consequence is that\nwe cannot change the pointer object, because it is constant. We can use the\npointer object in multiple ways, but we cannot change the\nmemory address that is inside this pointer object.\n\nHowever, if we mark the `pointer` object as a variable object,\nthen, we can change the memory address pointed by this `pointer` object.\nThe example below demonstrates that. Notice that the object pointed\nby the `pointer` object changes from `c1` to `c2`.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst c1: u8 = 5;\nconst c2: u8 = 6;\nvar pointer = &c1;\ntry stdout.print(\"{d}\\n\", .{pointer.*});\npointer = &c2;\ntry stdout.print(\"{d}\\n\", .{pointer.*});\n```\n:::\n\n\n\n\n\n```\n5\n6\n```\n\nThus, by setting the `pointer` object to a `var` or `const` object,\nyou specify if the memory address contained in this pointer object can change or not\nin your program. On the other side, you can change the value pointed by the pointer,\nif, and only if this value is stored in a variable object. If this value\nis in a constant object, then, you cannot change this value through a pointer.\n\n\n## Types of pointer\n\nIn Zig, there are two types of pointers [@zigdocs], which are:\n\n- single-item pointer (`*`);\n- many-item pointer (`[*]`);\n\n\nSingle-item pointer objects are objects whose data types are in the format `*T`.\nSo, for example, if an object have a data type `*u32`, it means that, this\nobject contains a single-item pointer that points to an unsigned 32-bit integer value.\nAs another example, if an object have type `*User`, then, it contains\na single-item pointer to an `User` value.\n\nIn contrast, many-item pointers are objects whose data types are in the format `[*]T`.\nNotice that the star symbol (`*`) is now inside a pair of brackets (`[]`). If the star\nsymbol is inside a pair of brackets, you know that this object is a many-item pointer.\n\nWhen you apply the `&` operator over an object, you will always get a single-item pointer.\nMany-item pointers are more of a \"internal type\" of the language, more closely\nrelated to slices. So, when you deliberately create a pointer with the `&` operator,\nyou always get a single-item pointer as result.\n\n\n\n## Pointer arithmetic\n\nPointer arithmetic is available in Zig, and they work the same way they work in C.\nWhen you have a pointer that points to an array, the pointer usually points to\nthe first element in the array, and you can use pointer arithmetic to\nadvance this pointer and access the other elements in the array.\n\n\nNotice in the example below, that initially, the `ptr` object was pointing\nto the first element in the array `ar`. But then, I started to walk through the array, by advancing\nthe pointer with simple pointer arithmetic.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ar = [_]i32{ 1, 2, 3, 4 };\nvar ptr: [*]const i32 = &ar;\ntry stdout.print(\"{d}\\n\", .{ptr[0]});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr[0]});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr[0]});\n```\n:::\n\n\n\n\n\n```\n1\n2\n3\n```\n\nAlthough you can create a pointer to an array like that, and\nstart to walk through this array by using pointer arithmetic,\nin Zig, we prefer to use slices, which were presented at @sec-arrays.\n\nBehind the hood, slices already are pointers,\nand they also come with the `len` property, which indicates\nhow many elements are in the slice. This is good because the `zig` compiler\ncan use it to check for potential buffer overflows, and other problems like that.\n\nAlso, you don't need to use pointer arithmetic to walk through the elements\nof a slice. You can simply use the `slice[index]` syntax to directly access\nany element you want in the slice.\nAs I mentioned at @sec-arrays, you can get a slice from an array by using\na range selector inside brackets. In the example below, I'm creating\na slice (`sl`) that covers the entire `ar` array. I can access any\nelement of `ar` from this slice, and, the slice itself already is a pointer\nbehind the hood.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst ar = [_]i32{1,2,3,4};\nconst sl = ar[0..ar.len];\n_ = sl;\n```\n:::\n\n\n\n\n\n\n## Optionals and Optional Pointers\n\nLet's talk about optionals and how they relate to pointers in Zig.\nBy default, objects in Zig are **non-nullable**. This means that, in Zig,\nyou can safely assume that any object in your source code is not null.\n\nThis is a powerful feature of Zig when you compare it to the developer experience in C.\nBecause in C, any object can be null at any point, and, as consequence, a pointer in C\nmight point to a null value. This is a common source of undefined behaviour in C.\nWhen programmers work with pointers in C, they have to constantly check if\ntheir pointers are pointing to null values or not.\n\nIf for some reason, your Zig code produces a null value somewhere, and, this null\nvalue ends up in an object that is non-nullable, a runtime error is always\nraised by your Zig program. Take the program below as an example.\nThe `zig` compiler can see the `null` value at compile time, and, as result,\nit raises a compile time error. But, if a `null` value is raised during\nruntime, a runtime error is also raised by the Zig program, with a\n\"attempt to use null value\" message.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar number: u8 = 5;\nnumber = null;\n```\n:::\n\n\n\n\n```\np5.zig:5:14: error: expected type 'u8',\n found '@TypeOf(null)'\n number = null;\n ^~~~\n```\n\n\nYou don't get this type of safety in C.\nIn C, you don't get warnings or errors about null values being produced in your program.\nIf for some reason, your code produces a null value in C, most of the times, you end up getting a segmentation fault error\nas result, which can mean many things.\nThat is why programmers have to constantly check for null values in C.\n\nPointers in Zig are also, by default, **non-nullable**. This is another amazing\nfeature in Zig. So, you can safely assume that any pointer that you create in\nyour Zig code is pointing to a non-null value.\nTherefore, you don't have this heavy work of checking if the pointers you create\nin Zig are pointing to a null value.\n\n\n### What are optionals?\n\nOk, we know now that all objects are non-nullable by default in Zig.\nBut what if we actually need to use an object that might receive a null value?\nHere is where optionals come in.\n\nAn optional object in Zig is an object that can be null.\nTo mark an object as optional, we use the `?` operator. When you put\nthis `?` operator right before the data type of an object, you transform\nthis data type into an optional data type, and the object becomes an optional object.\n\nTake the snippet below as an example. We are creating a new variable object\ncalled `num`. This object have the data type `?i32`, which means that,\nthis object contains either a signed 32-bit integer (`i32`), or, a null value.\nBoth alternatives are valid values to the `num` object.\nThat is why, I can actually change the value of this object to null, and,\nno errors are raised by the `zig` compiler, as demonstrated below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar num: ?i32 = 5;\nnum = null;\n```\n:::\n\n\n\n\n\n### Optional pointers\n\nYou can also mark a pointer object as an optional pointer, meaning that,\nthis object contains either a null value, or, a pointer that points to a value.\nWhen you mark a pointer as optional, the data type of this pointer object\nbecomes `?*const T` or `?*T`, depending if the value pointed by the pointer\nis a constant value or not. The `?` identifies the object as optional, while\nthe `*` identifies it as a pointer object.\n\nIn the example below, we are creating a variable object named `num`, and an\noptional pointer object named `ptr`. Notice that the data type of the object\n`ptr` indicates that it is either a null value, or a pointer to an `i32` value.\nAlso, notice that the pointer object (`ptr`) can be marked as optional, even if\nthe object `num` is not optional.\n\nWhat this code tells us is that, the `num` variable will never contain a null value.\nThis variable will always contain a valid `i32` value. But in contrast, the `ptr` object might contain either a null\nvalue, or, a pointer to an `i32` value.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar num: i32 = 5;\nvar ptr: ?*i32 = #\nptr = null;\nnum = 6;\n```\n:::\n\n\n\n\n\nBut what happens if we turn the table, and mark the `num` object as optional,\ninstead of the pointer object. If we do that, then, the pointer object is\nnot optional anymore. It would be a similar (although different) result. Because then, we would have\na pointer to an optional value. In other words, a pointer to a value that is either a\nnull value, or, a not-null value.\n\nIn the example below, we are recreating this idea. Now, the `ptr` object\nhave a data type of `*?i32`, instead of `?*i32`. Notice that the `*` symbol comes before of `?`\nthis time. So now, we have a pointer that points to a value that is either null\n, or, a signed 32-bit integer.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar num: ?i32 = 5;\n// ptr have type `*?i32`, instead of `?*i32`.\nconst ptr = #\n_ = ptr;\n```\n:::\n\n\n\n\n\n\n### Null handling in optionals {#sec-null-handling}\n\nWhen you have an optional object in your Zig code, you have to explicitly handle\nthe possibility of this object being null. It is like error-handling with `try` and `catch`.\nIn Zig you also have to handle null values like if they were a type of error.\n\nWe can do that, by using either:\n\n- an if statement, like you would do in C.\n- the `orelse` keyword.\n- unwrap the optional value with the `?` method.\n\nWhen you use an if statement, you use a pair of pipes\nto unwrap the optional value, and use this \"unwrapped object\"\ninside the if block.\nUsing the example below as a reference, if the object `num` is null,\nthen, the code inside the if statement is not executed. Otherwise,\nthe if statement will unwrap the object `num` into the `not_null_num`\nobject. This `not_null_num` object is guaranteed to be not null inside\nthe scope of the if statement.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst num: ?i32 = 5;\nif (num) |not_null_num| {\n try stdout.print(\"{d}\\n\", .{not_null_num});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n5\n```\n\n\n:::\n:::\n\n\n\n\n\nNow, the `orelse` keyword behaves like a binary operator. You connect two expressions with this keyword.\nOn the left side of `orelse`, you provide the expression that might result\nin a null value, and on the right side of `orelse`, you provide another expression\nthat will not result in a null value.\n\nThe idea behind the `orelse` keyword is: if the expression on the left side\nresult in a not-null value, then, this not-null value is used. However,\nif this expression on the left side result in a null value, then, the value\nof the expression on the right side is used instead.\n\nLooking at the example below, since the `x` object is currently null, the\n`orelse` decided to use the alternative value, which is the number 15.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst x: ?i32 = null;\nconst dbl = (x orelse 15) * 2;\ntry stdout.print(\"{d}\\n\", .{dbl});\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n30\n```\n\n\n:::\n:::\n\n\n\n\n\nYou can use the if statement or the `orelse` keyword, when you want to\nsolve (or deal with) this null value. However, if there is no clear solution\nto this null value, and the most logic and sane path is to simply panic\nand raise a loud error in your program when this null value is encountered,\nyou can use the `?` method of your optional object.\n\nIn essence, when you use this `?` method, the optional object is unwrapped.\nIf a not-null value is found in the optional object, then, this not-null value is used.\nOtherwise, the `unreachable` keyword is used. You can read more about this\n[`unreacheable` keyword at the official documentation](https://ziglang.org/documentation/master/#unreachable)[^un-docs].\nBut in essence, when you build your Zig source code using the build modes `ReleaseSafe` or `Debug`, this\n`unreacheable` keyword causes the program to panic and raise an error during runtime,\nlike in the example below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\nfn return_null(n: i32) ?i32 {\n if (n == 5) return null;\n return n;\n}\n\npub fn main() !void {\n const x: i32 = 5;\n const y: ?i32 = return_null(x);\n try stdout.print(\"{d}\\n\", .{y.?});\n}\n```\n:::\n\n\n\n\n\n```\nthread 12767 panic: attempt to use null value\np7.zig:12:34: 0x103419d in main (p7):\n try stdout.print(\"{d}\\n\", .{y.?});\n ^\n```\n\n\n[^un-docs]: .\n\n\n",
+ "supporting": [],
"filters": [
"rmarkdown/pagebreak.lua"
],
diff --git a/docs/Chapters/05-pointers.html b/docs/Chapters/05-pointers.html
index d14f04a5..1bc398f2 100644
--- a/docs/Chapters/05-pointers.html
+++ b/docs/Chapters/05-pointers.html
@@ -426,13 +426,13 @@
Pointer arithmetic is available in Zig, and they work the same way they work in C. When you have a pointer that points to an array, the pointer usually points to the first element in the array, and you can use pointer arithmetic to advance this pointer and access the other elements in the array.
Notice in the example below, that initially, the ptr object was pointing to the first element in the array ar. But then, I started to walk through the array, by advancing the pointer with simple pointer arithmetic.
1
2
diff --git a/docs/search.json b/docs/search.json
index 1999409e..d9730825 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -514,7 +514,7 @@
"href": "Chapters/05-pointers.html#pointer-arithmetic",
"title": "6 Pointers and Optionals",
"section": "6.3 Pointer arithmetic",
- "text": "6.3 Pointer arithmetic\nPointer arithmetic is available in Zig, and they work the same way they work in C. When you have a pointer that points to an array, the pointer usually points to the first element in the array, and you can use pointer arithmetic to advance this pointer and access the other elements in the array.\nNotice in the example below, that initially, the ptr object was pointing to the first element in the array ar. But then, I started to walk through the array, by advancing the pointer with simple pointer arithmetic.\n\nconst ar = [_]i32{1,2,3,4};\nvar ptr = &ar;\ntry stdout.print(\"{d}\\n\", .{ptr.*});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr.*});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr.*});\n\n1\n2\n3\nAlthough you can create a pointer to an array like that, and start to walk through this array by using pointer arithmetic, in Zig, we prefer to use slices, which were presented at Section 1.6.\nBehind the hood, slices already are pointers, and they also come with the len property, which indicates how many elements are in the slice. This is good because the zig compiler can use it to check for potential buffer overflows, and other problems like that.\nAlso, you don’t need to use pointer arithmetic to walk through the elements of a slice. You can simply use the slice[index] syntax to directly access any element you want in the slice. As I mentioned at Section 1.6, you can get a slice from an array by using a range selector inside brackets. In the example below, I’m creating a slice (sl) that covers the entire ar array. I can access any element of ar from this slice, and, the slice itself already is a pointer behind the hood.\n\nconst ar = [_]i32{1,2,3,4};\nconst sl = ar[0..ar.len];\n_ = sl;",
+ "text": "6.3 Pointer arithmetic\nPointer arithmetic is available in Zig, and they work the same way they work in C. When you have a pointer that points to an array, the pointer usually points to the first element in the array, and you can use pointer arithmetic to advance this pointer and access the other elements in the array.\nNotice in the example below, that initially, the ptr object was pointing to the first element in the array ar. But then, I started to walk through the array, by advancing the pointer with simple pointer arithmetic.\n\nconst ar = [_]i32{ 1, 2, 3, 4 };\nvar ptr: [*]const i32 = &ar;\ntry stdout.print(\"{d}\\n\", .{ptr[0]});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr[0]});\nptr += 1;\ntry stdout.print(\"{d}\\n\", .{ptr[0]});\n\n1\n2\n3\nAlthough you can create a pointer to an array like that, and start to walk through this array by using pointer arithmetic, in Zig, we prefer to use slices, which were presented at Section 1.6.\nBehind the hood, slices already are pointers, and they also come with the len property, which indicates how many elements are in the slice. This is good because the zig compiler can use it to check for potential buffer overflows, and other problems like that.\nAlso, you don’t need to use pointer arithmetic to walk through the elements of a slice. You can simply use the slice[index] syntax to directly access any element you want in the slice. As I mentioned at Section 1.6, you can get a slice from an array by using a range selector inside brackets. In the example below, I’m creating a slice (sl) that covers the entire ar array. I can access any element of ar from this slice, and, the slice itself already is a pointer behind the hood.\n\nconst ar = [_]i32{1,2,3,4};\nconst sl = ar[0..ar.len];\n_ = sl;",
"crumbs": [
"6Pointers and Optionals"
]
From e2491f6907bf9049a7445eea128d67af10ef0372 Mon Sep 17 00:00:00 2001
From: Niklas Johansson
Date: Fri, 14 Feb 2025 08:41:11 +0100
Subject: [PATCH 065/151] fix: Remove unnecessary swear word
While reading the physical book I came across an unnecessary swear word.
Personally, I am not against swear words but this one felt unnecessary
and makes the book feel less professional.
I updated both the string and base64 encoding.
Thank you for writing this book.
I assign the copyright of this contribution to Pedro Duarte Faria.
---
Chapters/01-base64.qmd | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Chapters/01-base64.qmd b/Chapters/01-base64.qmd
index fabfc797..962f63bb 100644
--- a/Chapters/01-base64.qmd
+++ b/Chapters/01-base64.qmd
@@ -834,8 +834,8 @@ var fba = std.heap.FixedBufferAllocator.init(
);
const allocator = fba.allocator();
-const text = "Testing some more shit";
-const etext = "VGVzdGluZyBzb21lIG1vcmUgc2hpdA==";
+const text = "Testing some more stuff";
+const etext = "VGVzdGluZyBzb21lIG1vcmUgc3R1ZmY=";
const base64 = Base64.init();
const encoded_text = try base64.encode(
allocator, text
@@ -852,8 +852,8 @@ try stdout.print(
```
```
-Encoded text: VGVzdGluZyBzb21lIG1vcmUgc2hpdA==
-Decoded text: Testing some more shit
+Encoded text: VGVzdGluZyBzb21lIG1vcmUgc3R1ZmY=
+Decoded text: Testing some more stuff
```
You can also see the full source code at once, by visiting the official repository of this book[^repo].
From 139721f72172e219e769fc9b87bb0f662ca87f22 Mon Sep 17 00:00:00 2001
From: Maarten Coppens
Date: Sat, 15 Feb 2025 11:39:01 +0100
Subject: [PATCH 066/151] Update 01-zig-weird.qmd
fix typo
---
Chapters/01-zig-weird.qmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd
index 125bdad3..1a1afa74 100644
--- a/Chapters/01-zig-weird.qmd
+++ b/Chapters/01-zig-weird.qmd
@@ -58,7 +58,7 @@ C are a frequent source of confusion. They really make it sometimes hard to debu
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 the code are being sent to the compiler, i.e.
-they obscures the actual source code that you wrote.
+they obscure the actual source code that you wrote.
You don't have macros in Zig. In Zig, the code you write, is the actual code that get's compiled by the compiler.
You also don't have a hidden control flow happening behind the scenes. And, you also
From 2294c2d9fef7b4d29898c45117b2ea548b00ae52 Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sat, 15 Feb 2025 20:11:12 -0300
Subject: [PATCH 067/151] Recompile book with changes
---
_freeze/Chapters/01-base64/execute-results/html.json | 4 ++--
_freeze/Chapters/01-zig-weird/execute-results/html.json | 8 +++++---
docs/Chapters/01-base64.html | 8 ++++----
docs/Chapters/01-zig-weird.html | 2 +-
docs/index.html | 4 ++--
docs/search.json | 4 ++--
6 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/_freeze/Chapters/01-base64/execute-results/html.json b/_freeze/Chapters/01-base64/execute-results/html.json
index 0965e634..9eb2b631 100644
--- a/_freeze/Chapters/01-base64/execute-results/html.json
+++ b/_freeze/Chapters/01-base64/execute-results/html.json
@@ -1,8 +1,8 @@
{
- "hash": "225506bc4affdcc5aaf897c63caf73a4",
+ "hash": "f1304b563ed8036a5bbeaa9b658cfdf0",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n# Project 1 - Building a base64 encoder/decoder {#sec-base64}\n\nAs our first small project, I want to implement a base64 encoder/decoder with you.\nBase64 is an encoding system which translates binary data to text.\nA big chunk of the web uses base64 to deliver binary data to systems\nthat can only read text data.\n\nThe most common example of a modern use case for base64 is essentially any email system,\nlike GMail, Outlook, etc. Because email systems normally use\nthe Simple Mail Transfer Protocol (SMTP), which is a web protocol\nthat supports only text data. So, if you need, for any reason, to\nsend a binary file (like for example, a PDF, or an Excel file) as\nan attachment in your email, these binary files are normally\nconverted to base64, before they are included in the SMTP message.\nSo, the base64 encoding is largely used in these email systems to include\nbinary data into the SMTP message.\n\n\n\n\n\n\n## How the base64 algorithm work?\n\nBut how exactly does the algorithm behind the base64 encoding work? Let's discuss that. First, I will\nexplain the base64 scale, which is the 64-character scale that is the basis for\nthe base64 encoding system.\n\nAfter that, I explain the algorithm behind a base64 encoder, which is the part of the algorithm that is responsible for encoding messages\ninto the base64 encoding system. Then, after that, I explain the algorithm behind a base64 decoder, which is\nthe part of the algorithm that is responsible for translating base64 messages back into their original meaning.\n\nIf you are unsure about the differences between an \"encoder\" and a \"decoder\",\ntake a look at @sec-encode-vs-decode.\n\n\n### The base64 scale {#sec-base64-scale}\n\nThe base64 encoding system is based on a scale that goes from 0 to 63 (hence the name).\nEach index in this scale is represented by a character (it is a scale of 64 characters).\nSo, in order to convert some binary data, to the base64 encoding, we need to convert each binary number to the corresponding\ncharacter in this \"scale of 64 characters\".\n\nThe base64 scale starts with all ASCII uppercase letters (A to Z) which represents\nthe first 25 indexes in this scale (0 to 25). After that, we have all ASCII lowercase letters\n(a to z), which represents the range 26 to 51 in the scale. After that, we\nhave the one digit numbers (0 to 9), which represents the indexes from 52 to 61 in the scale.\nFinally, the last two indexes in the scale (62 and 63) are represented by the characters `+` and `/`,\nrespectively.\n\nThese are the 64 characters that compose the base64 scale. The equal sign character (`=`) is not part of the scale itself,\nbut it is a special character in the base64 encoding system. This character is used solely as a suffix, to mark the end of the character sequence,\nor, to mark the end of meaningful characters in the sequence.\n\nThe bullet points below summarises the base64 scale:\n\n- range 0 to 25 is represented by: ASCII uppercase letters `-> [A-Z]`;\n- range 26 to 51 is represented by: ASCII lowercase letters `-> [a-z]`;\n- range 52 to 61 is represented by: one digit numbers `-> [0-9]`;\n- index 62 and 63 are represented by the characters `+` and `/`, respectively;\n- the character `=` represents the end of meaningful characters in the sequence;\n\n\n\n\n### Creating the scale as a lookup table {#sec-base64-table}\n\nThe best way to represent this scale in code, is to represent it as a *lookup table*.\nLookup tables are a classic strategy in computer science to speed up calculations. The basic idea\nis to replace a runtime calculation (which can take a long time to be done) with a basic array indexing\noperation.\n\nInstead of calculating the results everytime you need them, you calculate all possible results at once, and then, you store them in an array\n(which behaves lake a \"table\"). Then, every time you need to use one of the characters in the base64 scale, instead of\nusing many resources to calculate the exact character to be used, you simply retrieve this character\nfrom the array where you stored all the possible characters in the base64 scale.\nWe retrieve the character that we need directly from memory.\n\nWe can start building a Zig struct to store our base64 decoder/encoder logic.\nWe start with the `Base64` struct below. For now, we only have one single data member in this\nstruct, i.e. the member `_table`, which represents our lookup table. We also have an `init()` method,\nto create a new instance of a `Base64` object, and, a `_char_at()` method, which is a\n\"get character at index $x$\" type of function.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst Base64 = struct {\n _table: *const [64]u8,\n\n pub fn init() Base64 {\n const upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n const lower = \"abcdefghijklmnopqrstuvwxyz\";\n const numbers_symb = \"0123456789+/\";\n return Base64{\n ._table = upper ++ lower ++ numbers_symb,\n };\n }\n\n pub fn _char_at(self: Base64, index: usize) u8 {\n return self._table[index];\n }\n};\n```\n:::\n\n\n\n\n\nIn other words, the `_char_at()` method is responsible for getting the character in the lookup\ntable (i.e. the `_table` struct data member) that corresponds to a particular index in the\n\"base64 scale\". So, in the example below, we know that the character that corresponds to the\nindex 28 in the \"base64 scale\" is the character \"c\".\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst base64 = Base64.init();\ntry stdout.print(\n \"Character at index 28: {c}\\n\",\n .{base64._char_at(28)}\n);\n```\n:::\n\n\n\n\n```\nCharacter at index 28: c\n```\n\n\n\n### A base64 encoder {#sec-base64-encoder-algo}\n\nThe algorithm behind a base64 encoder usually works on a window of 3 bytes. Because each byte has\n8 bits, so, 3 bytes forms a set of $8 \\times 3 = 24$ bits. This is desirable for the base64 algorithm, because\n24 bits is divisible by 6, which forms $24 / 6 = 4$ groups of 6 bits each.\n\nTherefore, the base64 algorithm works by converting 3 bytes at a time\ninto 4 characters from the base64 scale. It keeps iterating through the input string,\n3 bytes at a time, and converting them into the base64 scale, producing 4 characters\nper iteration. It keeps iterating, and producing these \"new characters\"\nuntil it hits the end of the input string.\n\nNow, you may think, what if you have a particular string that have a number of bytes\nthat is not divisible by 3? What happens? For example, if you have a string\nthat contains only two characters/bytes, such as \"Hi\". How the\nalgorithm would behave in such situation? You find the answer at @fig-base64-algo1.\nYou can see at @fig-base64-algo1 that the string \"Hi\", when converted to base64,\nbecomes the string \"SGk=\":\n\n{#fig-base64-algo1}\n\nTaking the string \"Hi\" as an example, we have 2 bytes, or, 16 bits in total. So, we lack a full byte (8 bits)\nto complete the window of 24 bits that the base64 algorithm likes to work on. The first thing that\nthe algorithm does, is to check how to divide the input bytes into groups of 6 bits.\n\nIf the algorithm notices that there is a group of 6 bits that, have some bits in it, but, at the same time, it is not full\n(in other words, $0 < nbits < 6$, being $nbits$ the number of bits), meaning that, it lacks\nsome bits to fill the 6-bits requirement, the algorithm simply add extra zeros in this group\nto fill the space that it needs. That is why at @fig-base64-algo1, on the third group after the 6-bit transformation,\n2 extra zeros were added to fill the gap in this group.\n\nWhen we have a 6-bit group that is not completely full, like the third group, extra zeros\nare added to fill the gap. But what about when an entire 6-bit group is empty, or, it\nsimply doesn't exist? This is the case of the fourth 6-bit group exposed at\n@fig-base64-algo1.\n\nThis fourth group is necessary, because the algorithm works on 4 groups of 6 bits.\nBut the input string does not have enough bytes to create a fourth 6-bit group.\nEvery time this happens, where an entire group of 6 bits is empty,\nthis group becomes a \"padding group\". Every \"padding group\" is mapped to\nthe character `=` (equal sign), which represents \"null\", or, the end\nof meaningful characters in the sequence. Hence, everytime that the algorithm produces a\n\"padding group\", this group is automatically mapped to `=`.\n\nAs another example, if you give the string \"0\" as input to a base64 encoder, this string is\ntranslated into the base64 sequence \"MA==\".\nThe character \"0\" is, in binary, the sequence `00110000`[^zero-note]. So, with the 6-bit transformation\nexposed at @fig-base64-algo1, this single character would produce these two 6-bit groups: `001100`, `000000`.\nThe remaining two 6-bit groups become \"padding groups\". That is why the last\ntwo characters in the output sequence (MA==) are `==`.\n\n\n[^zero-note]: Notice that, the character \"0\" is different than the actual number 0, which is simply zero in binary.\n\n### A base64 decoder {#sec-base64-decoder-algo}\n\nThe algorithm behind a base64 decoder is essentially the inverse process of a base64 encoder.\nA base64 decoder needs to translate base64 messages back into their original meaning,\ni.e. into the original sequence of binary data.\n\nA base64 decoder usually works on a window of 4 bytes. Because it wants to convert these 4 bytes\nback into the original sequence of 3 bytes, that was converted into 4 groups of 6 bits by the\nbase64 encoder. Remember, in a base64 decoder we are essentially reverting the process made\nby the base64 encoder.\n\nEach byte in the input string (the base64 encoded string) normally contributes to re-create\ntwo different bytes in the output (the original binary data).\nIn other words, each byte that comes out of a base64 decoder is created by transforming merging two different\nbytes in the input together. You can visualize this relationship at @fig-base64-algo2:\n\n{#fig-base64-algo2}\n\nThe exact transformations, or, the exact steps applied to each byte from the input to transform them\ninto the bytes of the output, are a bit tricky to visualize in a figure like this. Because of that, I have\nsummarized these transformations as \"Some bit shifting and additions ...\" in the figure. These transformations\nwill be described in depth later.\n\nBesides that, if you look again at @fig-base64-algo2, you will notice that the character `=` was completely\nignored by the algorithm. Remember, this is just a special character that marks the end of meaningful characters\nin the base64 sequence. So, every `=` character in a base64 encoded sequence should be ignored by a base64 decoder.\n\n\n## Difference between encode and decode {#sec-encode-vs-decode}\n\nIf you don't have any previous experience with base64, you might not understand the differences\nbetween \"encode\" and \"decode\". Essentially, the terms \"encode\" and \"decode\" here\nhave the exact same meaning as they have in the field of encryption (i.e. they mean the same thing as \"encode\" and \"decode\" in hashing\nalgorithms, like the MD5 algorithm).\n\nThus, \"encode\" means that we want to encode, or, in other words, we want to translate some message into\nthe base64 encoding system. We want to produce the sequence of base64 characters that represent this\noriginal message in the base64 encoding system.\n\nIn contrast, \"decode\" represents the inverse process.\nWe want to decode, or, in other words, translate a base64 message back to its original content.\nSo, in this process we get a sequence of base64 characters as input, and produce as output,\nthe binary data that is represented by this sequence of base64 characters.\n\nAny base64 library is normally composed of these two parts: 1) the encoder, which is a function that encodes\n(i.e. it converts) any sequence of binary data into a sequence of base64 characters; 2) the decoder, which is a function\nthat converts a sequence of base64 characters back into the original sequence of binary data.\n\n\n\n## Calculating the size of the output {#sec-base64-length-out}\n\nOne task that we need to do is to calculate how much space we need to reserve for the\noutput, both of the encoder and decoder. This is simple math, and can be done easily in Zig\nbecause every array has its length (its number of elements) easily accesible by consulting\nthe `.len` property of the array.\n\nFor the encoder, the logic is the following: for each 3 bytes that we find in the input,\n4 new bytes are created in the output. So, we take the number of bytes in the input, divide it\nby 3, use a ceiling function, then, we multiply the result by 4. That way, we get the total\nnumber of bytes that will be produced by the encoder in its output.\n\nThe `_calc_encode_length()` function below encapsulates this logic.\nInside this function, we take the length of the input array,\nwe divide it by 3, and apply a ceil operation over the result by using the\n`divCeil()` function from the Zig Standard Library. Lastly, we multiply\nthe end result by 4 to get the answer we need.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn _calc_encode_length(input: []const u8) !usize {\n if (input.len < 3) {\n const n_output: usize = 4;\n return n_output;\n }\n const n_output: usize = try std.math.divCeil(\n usize, input.len, 3\n );\n return n_output * 4;\n}\n```\n:::\n\n\n\n\n\nAlso, you might have notice that, if the input length is less than 3 bytes, then, the output length of the encoder is\nalways 4 bytes. This is the case for every input with less than 3 bytes, because, as I described at @sec-base64-encoder-algo,\nthe algorithm always produces enough \"padding-groups\" in the end result, to complete the 4 bytes window.\n\nNow, for the decoder, we just need to apply the inverse logic: for each 4 bytes in the input, 3 bytes\nwill be produced in the output of the decoder. I mean, this is roughly true, because we also need to\ntake the `=` character into account, which is always ignored by the decoder, as we described at @sec-base64-decoder-algo, and,\nat @fig-base64-algo2. But we can ignore this fact for now, just to keep things simple.\n\nThe function `_calc_decode_length()` exposed below summarizes this logic that we described. It is very similar\nto the function `_calc_encode_length()`. Only the division part is twisted, and also, in the special\ncase where we have less than 4 bytes in the input to work on. Also notice that this time, we apply\na floor operation over the output of the division, by using the `divFloor()`\nfunction (instead of a ceiling operation with `divCeil()`).\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn _calc_decode_length(input: []const u8) !usize {\n if (input.len < 4) {\n const n_output: usize = 3;\n return n_output;\n }\n const n_output: usize = try std.math.divFloor(\n usize, input.len, 4\n );\n return n_output * 3;\n}\n```\n:::\n\n\n\n\n\n## Building the encoder logic {#sec-encoder-logic}\n\nIn this section, we can start building the logic behind the `encode()` function, which\nwill be responsible for encoding messages into the base64 encoding system.\nIf you are an anxious person, and you want to see now the full source code of the implementation\nfor this base64 encoder/decoder, you can find it at the `ZigExamples` folder in the official repository of\nthis book[^zig-base64-algo].\n\n[^zig-base64-algo]: .\n\n\n\n### The 6-bit transformation {#sec-6bit-transf}\n\nThe 6-bit transformation presented at @fig-base64-algo1 is the core part of the base64 encoder algorithm.\nBy understanding how this transformation is made in code, the rest of the algorithm becomes much simpler\nto comprehend.\n\nIn essence, this 6-bit transformation is made with the help of bitwise operators.\nBitwise operators are essential to any type of low-level operation that is done at the bit-level. For the specific case of the base64 algorithm,\nthe operators *bif shift to the left* (`<<`), *bit shift to the right* (`>>`), and the *bitwise and* (`&`) are used. They\nare the core solution for the 6-bit transformation.\n\nThere are 3 different scenarios that we need to take into account in this transformation. First, is the perfect scenario,\nwhere we have the perfect window of 3 bytes to work on. Second, we have the scenario where we have a window of only\ntwo bytes to work with. And last, we have the scenario where we have a window of one single byte.\n\nIn each of these 3 scenarios, the 6-bit transformation works a bit differently. To make the explanation\neasier, I will use the variable `output` to refer to the bytes in the output of the base64 encoder,\nand the variable `input` to refer to the bytes in the input of the encoder.\n\n\nSo, if you have the perfect window of 3 bytes, these are steps for the 6-bit transformation:\n\n1. `output[0]` is produced by moving the bits from `input[0]` two positions to the right.\n1. `output[1]` is produced by summing two components. First, take the last two bits from `input[0]`, then, move them four positions to the left. Second, move the bits from `input[1]` four positions to the right. Sum these two components.\n1. `output[2]` is produced by summing two components. First, take the last four bits from `input[1]`, then, move them two positions to the left. Second, move the bits from `input[2]` six positions to the right. Sum these two components.\n1. `output[3]` is produced by taking the last six bits from `input[2]`.\n\n\nThis is the perfect scenario, where we have a full window of 3 bytes to work on.\nJust to make things as clear as possible, the @fig-encoder-bitshift demonstrates visually how\nthe step 2 mentioned above works. So the 2nd byte in the `output` of the encoder, is made by taking the 1st byte (dark purple)\nand the 2nd byte (orange) from the input. You can see that, at the end of the process, we get a new\nbyte that contains the last 2 bits from the 1st byte in the `input`, and the first 4 bits\nfrom the 2nd byte in the `input`.\n\n{#fig-encoder-bitshift}\n\nOn the other hand, we must be prepared for the instances where we do not have the perfect window of 3 bytes.\nIf you have a window of 2 bytes, then, the steps 3 and 4, which produces the bytes `output[2]` and `output[3]`, change a little bit,\nand they become:\n\n- `output[2]` is produced by taking the last 4 bits from `input[1]`, then, move them two positions to the left.\n- `output[3]` is the character `'='`.\n\n\nFinally, if you have a window of a single byte, then, the steps 2 to 4, which produces the bytes `output[1]`, `output[2]` and `output[3]` change,\nbecoming:\n\n- `output[1]` is produced by taking the last two bits from `input[0]`, then, move them four positions to the left.\n- `output[2]` and `output[3]` are the character `=`.\n\n\nIf these bullet points were a bit confusing for you, you may find the @tbl-transf-6bit more intuitive.\nThis table unifies all this logic into a simple table. Notice that\nthis table also provides the exact expression in Zig that creates the corresponding\nbyte in the output.\n\n\n::: {#tbl-transf-6bit}\n\n| Number of bytes in the window | Byte index in the output | In code |\n|-------------------------------|--------------------------|--------------------------------------------|\n| 3 | 0 | input[0] >> 2 |\n| 3 | 1 | ((input[0] & 0x03) << 4) + (input[1] >> 4) |\n| 3 | 2 | ((input[1] & 0x0f) << 2) + (input[2] >> 6) |\n| 3 | 3 | input[2] & 0x3f |\n| 2 | 0 | input[0] >> 2 |\n| 2 | 1 | ((input[0] & 0x03) << 4) + (input[1] >> 4) |\n| 2 | 2 | ((input[1] & 0x0f) << 2) |\n| 2 | 3 | '=' |\n| 1 | 0 | input[0] >> 2 |\n| 1 | 1 | ((input[0] & 0x03) << 4) |\n| 1 | 2 | '=' |\n| 1 | 3 | '=' |\n\n: How the 6-bit transformation translates into code in different window settings.\n\n:::\n\n\n\n\n\n\n### Bit-shifting in Zig\n\nBit-shifting in Zig works similarly to bit-shifting in C.\nAll bitwise operators that exist in C are available in Zig.\nHere, in the base64 encoder algorithm, they are essential\nto produce the result we want.\n\nFor those who are not familiar with these operators, they are\noperators that operates at the bit-level of your values.\nThis means that these operators takes the bits that form the value\nyou have, and change them in some way. This ultimately also changes\nthe value itself, because the binary representation of this value\nchanges.\n\nWe have already seen at @fig-encoder-bitshift the effect produced by a bit-shift.\nBut let's use the first byte in the output of the base64 encoder as another example of what\nbit-shifting means. This is the easiest byte of the 4 bytes in the output\nto build. Because we only need to move the bits from the first byte in the input two positions to the right,\nwith the *bit shift to the right* (`>>`) operator.\n\nIf we take the string \"Hi\" that we used at @fig-base64-algo1 as an example, the first byte in\nthis string is \"H\", which is the sequence `01001000` in binary.\nIf we move the bits of this byte, two places to the right, we get the sequence `00010010` as result.\nThis binary sequence is the value `18` in decimal, and also, the value `0x12` in hexadecimal.\nNotice that the first 6 bits of \"H\" were moved to the end of the byte.\nWith this operation, we get the first byte of the output.\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 input = \"Hi\";\n try stdout.print(\"{d}\\n\", .{input[0] >> 2});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n18\n```\n\n\n:::\n:::\n\n\n\n\nIf you recall @fig-base64-algo1, the first byte present in the output should\nbe equivalent to the 6-bit group `010010`. Although being visually different, the\nsequences `010010` and `00010010` are semantically equal. They mean the same thing.\nThey both represent the number 18 in decimal, and the value `0x12` in hexadecimal.\n\nSo, don't take the \"6-bit group\" factor so seriously. We do not need necessarily to\nget a 6-bit sequence as result. As long as the meaning of the 8-bit sequence we get is the same\nof the 6-bit sequence, we are in the clear.\n\n\n\n### Selecting specific bits with the `&` operator\n\nIf you comeback to @sec-6bit-transf, you will see that, in order to produce\nthe second and third bytes in the output, we need to select specific\nbits from the first and second bytes in the input string. But how\ncan we do that? The answer relies on the *bitwise and* (`&`) operator.\n\nThe @fig-encoder-bitshift already showed you what effect this `&` operator\nproduces in the bits of its operands. But let's make a clear description of it.\n\nIn summary, the `&` operator performs a logical conjunction operation\nbetween the bits of its operands. In more details, the operator `&`\ncompares each bit of the first operand to the corresponding bit of the second operand.\nIf both bits are 1, the corresponding result bit is set to 1.\nOtherwise, the corresponding result bit is set to 0 [@microsoftbitwiseand].\n\nSo, if we apply this operator to the binary sequences `1000100` and `00001101`\nthe result of this operation is the binary sequence `00000100`. Because only\nat the sixth position in both binary sequences we had a 1 value. So any\nposition where we do not have both binary sequences setted to 1, we get\na 0 bit in the resulting binary sequence.\n\nWe lose information about the original bit values\nfrom both sequences in this case. Because we no longer know\nif this 0 bit in the resulting binary sequence was produced by\ncombining 0 with 0, or 1 with 0, or 0 with 1.\n\nAs an example, suppose you have the binary sequence `10010111`, which is the number 151 in decimal. How\ncan we get a new binary sequence which contains only the third and\nfourth bits of this sequence?\n\nWe just need to combine this sequence with `00110000` (is `0x30` in hexadecimal) using the `&` operator.\nNotice that only the third and fourth positions in this binary sequence is setted to 1. As a consequence, only the\nthird and fourth values of both binary sequences are potentially preserved in the output. All the remaining positions\nare setted to zero in the output sequence, which is `00010000` (is the number 16 in decimal).\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 bits = 0b10010111;\n try stdout.print(\"{d}\\n\", .{bits & 0b00110000});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n16\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Allocating space for the output\n\nAs I described at @sec-stack, to store an object in the stack,\nthis object needs to have a known and fixed length at compile-time. This is an important\nlimitation for our base64 encoder/decoder case. Because the size of\nthe output (from both the encoder and decoder) depends\ndirectly on the size of the input.\n\nHaving this in mind, we cannot know at compile time which is\nthe size of the output for both the encoder and decoder.\nSo, if we can't know the size of the output at compile time,\nthis means that we cannot store the output for both the encoder\nand decoder in the stack.\n\nConsequently, we need to store this output on the heap,\nand, as I commented at @sec-heap, we can only\nstore objects in the heap by using allocator objects.\nSo, one the arguments to both the `encode()` and `decode()`\nfunctions, needs to be an allocator object, because\nwe know for sure that, at some point inside the body of these\nfunctions, we need to allocate space on the heap to\nstore the output of these functions.\n\nThat is why, both the `encode()` and `decode()` functions that I\npresent in this book, have an argument called `allocator`,\nwhich receives a allocator object as input, identified by\nthe type `std.mem.Allocator` from the Zig Standard Library.\n\n\n\n### Writing the `encode()` function\n\nNow that we have a basic understanding on how the bitwise operators work, and how\nexactly they help us to achieve the result we want to achieve. We can now encapsulate\nall the logic that we have described at @fig-base64-algo1 and @tbl-transf-6bit into a nice\nfunction that we can add to our `Base64` struct definition, that we started at @sec-base64-table.\n\nYou can find the `encode()` function below. Notice that the first argument of this function,\nis the `Base64` struct itself. Therefore, this argument clearly signals\nthat this function is a method from the `Base64` struct.\n\nBecause the `encode()` function itself is fairly long,\nI intentionally omitted the `Base64` struct definition in this source code,\njust for brevity reasons. So, just remember that this function is a public function (or a public method) from the\n`Base64` struct.\n\nFurthermore, this `encode()` function has two other arguments:\n\n1. `input` is the input sequence of characters that you want to encode in base64;\n1. `allocator` is an allocator object to use in the necessary memory allocations.\n\nI described everything you need to know about allocator objects at @sec-allocators.\nSo, if you are not familiar with them, I highly recommend you to comeback to\nthat section, and read it.\nBy looking at the `encode()` function, you will see that we use this\nallocator object to allocate enough memory to store the output of\nthe encoding process.\n\nThe main for loop in the function is responsible for iterating through the entire input string.\nIn every iteration, we use a `count` variable to count how many iterations we had at the\nmoment. When `count` reaches 3, then, we try to encode the 3 characters (or bytes) that we have accumulated\nin the temporary buffer object (`buf`).\n\nAfter encoding these 3 characters and storing the result in the `output` variable, we reset\nthe `count` variable to zero, and start to count again on the next iteration of the loop.\nIf the loop hits the end of the string, and, the `count` variable is less than 3, then, it means that\nthe temporary buffer contains the last 1 or 2 bytes from the input.\nThat is why we have two `if` statements after the for loop. To deal which each possible case.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub fn encode(self: Base64,\n allocator: std.mem.Allocator,\n input: []const u8) ![]u8 {\n\n if (input.len == 0) {\n return \"\";\n }\n\n const n_out = try _calc_encode_length(input);\n var out = try allocator.alloc(u8, n_out);\n var buf = [3]u8{ 0, 0, 0 };\n var count: u8 = 0;\n var iout: u64 = 0;\n\n for (input, 0..) |_, i| {\n buf[count] = input[i];\n count += 1;\n if (count == 3) {\n out[iout] = self._char_at(buf[0] >> 2);\n out[iout + 1] = self._char_at(\n ((buf[0] & 0x03) << 4) + (buf[1] >> 4)\n );\n out[iout + 2] = self._char_at(\n ((buf[1] & 0x0f) << 2) + (buf[2] >> 6)\n );\n out[iout + 3] = self._char_at(buf[2] & 0x3f);\n iout += 4;\n count = 0;\n }\n }\n\n if (count == 1) {\n out[iout] = self._char_at(buf[0] >> 2);\n out[iout + 1] = self._char_at(\n (buf[0] & 0x03) << 4\n );\n out[iout + 2] = '=';\n out[iout + 3] = '=';\n }\n\n if (count == 2) {\n out[iout] = self._char_at(buf[0] >> 2);\n out[iout + 1] = self._char_at(\n ((buf[0] & 0x03) << 4) + (buf[1] >> 4)\n );\n out[iout + 2] = self._char_at(\n (buf[1] & 0x0f) << 2\n );\n out[iout + 3] = '=';\n iout += 4;\n }\n\n return out;\n}\n```\n:::\n\n\n\n\n\n\n## Building the decoder logic {#sec-decoder-logic}\n\nNow, we can focus on writing the base64 decoder logic. Remember from @fig-base64-algo2 that,\na base64 decoder does the inverse process of an encoder. So, all we need to do, is to\nwrite a `decode()` function that performs the inverse process that I exposed at @sec-encoder-logic.\n\n\n### Mapping base64 characters to their indexes {#sec-map-base64-index}\n\nOne thing that we need to do, in order to decode a base64-encoded message, is to calculate\nthe index in the base64 scale of every base64 character that we encounter in the decoder input.\n\nIn other words, the decoder receives as input, a sequence of base64 characters. We need\nto translate this sequence of characters into a sequence of indexes. These indexes\nare the index of each character in the base64 scale. This way, we get the value/byte\nthat was calculated in the 6-bit transformation step of the encoder process.\n\nThere are probably better/faster ways to calculate this, especially using a \"divide and conquer\"\ntype of strategy. But for now, I am satisfied with a simple and \"brute force\" type of strategy.\nThe `_char_index()` function below contains this strategy.\n\nWe are essentially looping through the *lookup table* with the base64 scale,\nand comparing the character we got with each character in the base64 scale.\nIf these characters match, then, we return the index of this character in the\nbase64 scale as the result.\n\nNotice that, if the input character is `'='`, the function returns the index 64, which is\n\"out of range\" in the scale. But, as I described at @sec-base64-scale,\nthe character `'='` does not belong to the base64 scale itself.\nIt is a special and meaningless character in base64.\n\nAlso notice that this `_char_index()` function is a method from our `Base64` struct,\nbecause of the `self` argument. Again, I have omitted the `Base64` struct definition in this example\nfor brevity reasons.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfn _char_index(self: Base64, char: u8) u8 {\n if (char == '=')\n return 64;\n var index: u8 = 0;\n for (0..63) |i| {\n if (self._char_at(i) == char)\n break;\n index += 1;\n }\n\n return index;\n}\n```\n:::\n\n\n\n\n\n\n### The 6-bit transformation\n\nOnce again, the core part of the algorithm is the 6-bit transformation.\nIf we understand the necessary steps to perform this transformation, the rest\nof the algorithm becomes much easier.\n\nFirst of all, before we actually go to the 6-bit transformation,\nwe need to make sure that we use `_char_index()` to convert the sequence of base64 characters\ninto a sequence of indexes. So the snippet below is important for the job that will be done.\nThe result of `_char_index()` is stored in a temporary buffer, and this temporary buffer\nis what we are going to use in the 6-bit transformation, instead of the actual `input` object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (0..input.len) |i| {\n buf[i] = self._char_index(input[i]);\n}\n```\n:::\n\n\n\n\nNow, instead of producing 4 bytes (or 4 characters) as output per each window of 3 characters in the input,\na base64 decoder produces 3 bytes (or 3 characters) as output per each window of 4 characters in the input.\nOnce again, is the inverse process.\n\nSo, the steps to produce the 3 bytes in the output are:\n\n1. `output[0]` is produced by summing two components. First, move the bits from `buf[0]` two positions to the left. Second, move the bits from `buf[1]` 4 positions to the right. Then, sum these two components.\n1. `output[1]` is produced by summing two components. First, move the bits from `buf[1]` four positions to the left. Second, move the bits from `buf[2]` 2 positions to the right. Then, sum these two components.\n1. `output[2]` is produced by summing two components. First, move the bits from `buf[2]` six positions to the left. Then, you sum the result with `buf[3]`.\n\n\nBefore we continue, let's try to visualize how these transformations make the original bytes that we had\nbefore the encoding process. First, think back to the 6-bit transformation performed by the encoder exposed at @sec-encoder-logic.\nThe first byte in the output of the encoder is produced by moving the bits in the first byte of the input two positions to the right.\n\nIf for example the first byte in the input of the encoder was the sequence `ABCDEFGH`, then, the first byte in the output of the encoder would be\n`00ABCDEF` (this sequence would be the first byte in the input of the decoder). Now, if the second byte in the input of the encoder was the sequence\n`IJKLMNOP`, then, the second byte in the encoder output would be `00GHIJKL` (as we demonstrated at @fig-encoder-bitshift).\n\nHence, if the sequences `00ABCDEF` and `00GHIJKL` are the first and second bytes, respectively, in the input of the decoder, the\n@fig-decoder-bitshift demonstrates visually how these two bytes are transformed into the first byte of the output of the decoder.\nNotice that the output byte is the sequence `ABCDEFGH`, which is the original byte from the input of the encoder.\n\n{#fig-decoder-bitshift}\n\nThe @tbl-6bit-decode presents how the three steps described earlier translate into Zig code:\n\n\n\n::: {#tbl-6bit-decode}\n\n| Byte index in the output | In code |\n|--------------------------|-------------------------------|\n| 0 | (buf[0] << 2) + (buf[1] >> 4) |\n| 1 | (buf[1] << 4) + (buf[2] >> 2) |\n| 2 | (buf[2] << 6) + buf[3] |\n\n: The necessary steps for the 6-transformation in the decode process.\n\n\n:::\n\n\n\n\n\n\n\n### Writing the `decode()` function\n\nThe `decode()` function below contains the entire decoding process.\nWe first calculate the size of the output, with\n`_calc_decode_length()`, then, we allocate enough memory for this output with\nthe allocator object.\n\nThree temporary variables are created: 1) `count`, to hold the window count\nin each iteration of the for loop; 2) `iout`, to hold the current index in the output;\n3) `buf`, which is the temporary buffer that holds the base64 indexes to be\nconverted through the 6-bit transformation.\n\nThen, in each iteration of the for loop we fill the temporary buffer with the current\nwindow of bytes. When `count` hits the number 4, then, we have a full window of\nindexes in `buf` to be converted, and then, we apply the 6-bit transformation\nover the temporary buffer.\n\nNotice that we check if the indexes 2 and 3 in the temporary buffer are the number 64, which, if you recall\nfrom @sec-map-base64-index, is when the `_calc_index()` function receives a `'='` character\nas input. So, if these indexes are equal to the number 64, the `decode()` function knows\nthat it can simply ignore these indexes. They are not converted because, as I described before,\nthe character `'='` has no meaning, despite being the end of meaningful characters in the sequence.\nSo we can safely ignore them when they appear in the sequence.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfn decode(self: Base64,\n allocator: std.mem.Allocator,\n input: []const u8) ![]u8 {\n\n if (input.len == 0) {\n return \"\";\n }\n const n_output = try _calc_decode_length(input);\n var output = try allocator.alloc(u8, n_output);\n var count: u8 = 0;\n var iout: u64 = 0;\n var buf = [4]u8{ 0, 0, 0, 0 };\n\n for (0..input.len) |i| {\n buf[count] = self._char_index(input[i]);\n count += 1;\n if (count == 4) {\n output[iout] = (buf[0] << 2) + (buf[1] >> 4);\n if (buf[2] != 64) {\n output[iout + 1] = (buf[1] << 4) + (buf[2] >> 2);\n }\n if (buf[3] != 64) {\n output[iout + 2] = (buf[2] << 6) + buf[3];\n }\n iout += 3;\n count = 0;\n }\n }\n\n return output;\n}\n```\n:::\n\n\n\n\n\n## The end result\n\nNow that we have both `decode()` and `encode()` implemented. We have a fully functioning\nbase64 encoder/decoder implemented in Zig. Here is an usage example of our\n`Base64` struct with the `encode()` and `decode()` methods that we have implemented.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar memory_buffer: [1000]u8 = undefined;\nvar fba = std.heap.FixedBufferAllocator.init(\n &memory_buffer\n);\nconst allocator = fba.allocator();\n\nconst text = \"Testing some more shit\";\nconst etext = \"VGVzdGluZyBzb21lIG1vcmUgc2hpdA==\";\nconst base64 = Base64.init();\nconst encoded_text = try base64.encode(\n allocator, text\n);\nconst decoded_text = try base64.decode(\n allocator, etext\n);\ntry stdout.print(\n \"Encoded text: {s}\\n\", .{encoded_text}\n);\ntry stdout.print(\n \"Decoded text: {s}\\n\", .{decoded_text}\n);\n```\n:::\n\n\n\n\n```\nEncoded text: VGVzdGluZyBzb21lIG1vcmUgc2hpdA==\nDecoded text: Testing some more shit\n```\n\nYou can also see the full source code at once, by visiting the official repository of this book[^repo].\nMore precisely inside the `ZigExamples` folder[^zig-base64-algo].\n\n[^repo]: \n",
+ "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n# Project 1 - Building a base64 encoder/decoder {#sec-base64}\n\nAs our first small project, I want to implement a base64 encoder/decoder with you.\nBase64 is an encoding system which translates binary data to text.\nA big chunk of the web uses base64 to deliver binary data to systems\nthat can only read text data.\n\nThe most common example of a modern use case for base64 is essentially any email system,\nlike GMail, Outlook, etc. Because email systems normally use\nthe Simple Mail Transfer Protocol (SMTP), which is a web protocol\nthat supports only text data. So, if you need, for any reason, to\nsend a binary file (like for example, a PDF, or an Excel file) as\nan attachment in your email, these binary files are normally\nconverted to base64, before they are included in the SMTP message.\nSo, the base64 encoding is largely used in these email systems to include\nbinary data into the SMTP message.\n\n\n\n\n\n\n## How the base64 algorithm work?\n\nBut how exactly does the algorithm behind the base64 encoding work? Let's discuss that. First, I will\nexplain the base64 scale, which is the 64-character scale that is the basis for\nthe base64 encoding system.\n\nAfter that, I explain the algorithm behind a base64 encoder, which is the part of the algorithm that is responsible for encoding messages\ninto the base64 encoding system. Then, after that, I explain the algorithm behind a base64 decoder, which is\nthe part of the algorithm that is responsible for translating base64 messages back into their original meaning.\n\nIf you are unsure about the differences between an \"encoder\" and a \"decoder\",\ntake a look at @sec-encode-vs-decode.\n\n\n### The base64 scale {#sec-base64-scale}\n\nThe base64 encoding system is based on a scale that goes from 0 to 63 (hence the name).\nEach index in this scale is represented by a character (it is a scale of 64 characters).\nSo, in order to convert some binary data, to the base64 encoding, we need to convert each binary number to the corresponding\ncharacter in this \"scale of 64 characters\".\n\nThe base64 scale starts with all ASCII uppercase letters (A to Z) which represents\nthe first 25 indexes in this scale (0 to 25). After that, we have all ASCII lowercase letters\n(a to z), which represents the range 26 to 51 in the scale. After that, we\nhave the one digit numbers (0 to 9), which represents the indexes from 52 to 61 in the scale.\nFinally, the last two indexes in the scale (62 and 63) are represented by the characters `+` and `/`,\nrespectively.\n\nThese are the 64 characters that compose the base64 scale. The equal sign character (`=`) is not part of the scale itself,\nbut it is a special character in the base64 encoding system. This character is used solely as a suffix, to mark the end of the character sequence,\nor, to mark the end of meaningful characters in the sequence.\n\nThe bullet points below summarises the base64 scale:\n\n- range 0 to 25 is represented by: ASCII uppercase letters `-> [A-Z]`;\n- range 26 to 51 is represented by: ASCII lowercase letters `-> [a-z]`;\n- range 52 to 61 is represented by: one digit numbers `-> [0-9]`;\n- index 62 and 63 are represented by the characters `+` and `/`, respectively;\n- the character `=` represents the end of meaningful characters in the sequence;\n\n\n\n\n### Creating the scale as a lookup table {#sec-base64-table}\n\nThe best way to represent this scale in code, is to represent it as a *lookup table*.\nLookup tables are a classic strategy in computer science to speed up calculations. The basic idea\nis to replace a runtime calculation (which can take a long time to be done) with a basic array indexing\noperation.\n\nInstead of calculating the results everytime you need them, you calculate all possible results at once, and then, you store them in an array\n(which behaves lake a \"table\"). Then, every time you need to use one of the characters in the base64 scale, instead of\nusing many resources to calculate the exact character to be used, you simply retrieve this character\nfrom the array where you stored all the possible characters in the base64 scale.\nWe retrieve the character that we need directly from memory.\n\nWe can start building a Zig struct to store our base64 decoder/encoder logic.\nWe start with the `Base64` struct below. For now, we only have one single data member in this\nstruct, i.e. the member `_table`, which represents our lookup table. We also have an `init()` method,\nto create a new instance of a `Base64` object, and, a `_char_at()` method, which is a\n\"get character at index $x$\" type of function.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst Base64 = struct {\n _table: *const [64]u8,\n\n pub fn init() Base64 {\n const upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n const lower = \"abcdefghijklmnopqrstuvwxyz\";\n const numbers_symb = \"0123456789+/\";\n return Base64{\n ._table = upper ++ lower ++ numbers_symb,\n };\n }\n\n pub fn _char_at(self: Base64, index: usize) u8 {\n return self._table[index];\n }\n};\n```\n:::\n\n\n\n\n\nIn other words, the `_char_at()` method is responsible for getting the character in the lookup\ntable (i.e. the `_table` struct data member) that corresponds to a particular index in the\n\"base64 scale\". So, in the example below, we know that the character that corresponds to the\nindex 28 in the \"base64 scale\" is the character \"c\".\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst base64 = Base64.init();\ntry stdout.print(\n \"Character at index 28: {c}\\n\",\n .{base64._char_at(28)}\n);\n```\n:::\n\n\n\n\n```\nCharacter at index 28: c\n```\n\n\n\n### A base64 encoder {#sec-base64-encoder-algo}\n\nThe algorithm behind a base64 encoder usually works on a window of 3 bytes. Because each byte has\n8 bits, so, 3 bytes forms a set of $8 \\times 3 = 24$ bits. This is desirable for the base64 algorithm, because\n24 bits is divisible by 6, which forms $24 / 6 = 4$ groups of 6 bits each.\n\nTherefore, the base64 algorithm works by converting 3 bytes at a time\ninto 4 characters from the base64 scale. It keeps iterating through the input string,\n3 bytes at a time, and converting them into the base64 scale, producing 4 characters\nper iteration. It keeps iterating, and producing these \"new characters\"\nuntil it hits the end of the input string.\n\nNow, you may think, what if you have a particular string that have a number of bytes\nthat is not divisible by 3? What happens? For example, if you have a string\nthat contains only two characters/bytes, such as \"Hi\". How the\nalgorithm would behave in such situation? You find the answer at @fig-base64-algo1.\nYou can see at @fig-base64-algo1 that the string \"Hi\", when converted to base64,\nbecomes the string \"SGk=\":\n\n{#fig-base64-algo1}\n\nTaking the string \"Hi\" as an example, we have 2 bytes, or, 16 bits in total. So, we lack a full byte (8 bits)\nto complete the window of 24 bits that the base64 algorithm likes to work on. The first thing that\nthe algorithm does, is to check how to divide the input bytes into groups of 6 bits.\n\nIf the algorithm notices that there is a group of 6 bits that, have some bits in it, but, at the same time, it is not full\n(in other words, $0 < nbits < 6$, being $nbits$ the number of bits), meaning that, it lacks\nsome bits to fill the 6-bits requirement, the algorithm simply add extra zeros in this group\nto fill the space that it needs. That is why at @fig-base64-algo1, on the third group after the 6-bit transformation,\n2 extra zeros were added to fill the gap in this group.\n\nWhen we have a 6-bit group that is not completely full, like the third group, extra zeros\nare added to fill the gap. But what about when an entire 6-bit group is empty, or, it\nsimply doesn't exist? This is the case of the fourth 6-bit group exposed at\n@fig-base64-algo1.\n\nThis fourth group is necessary, because the algorithm works on 4 groups of 6 bits.\nBut the input string does not have enough bytes to create a fourth 6-bit group.\nEvery time this happens, where an entire group of 6 bits is empty,\nthis group becomes a \"padding group\". Every \"padding group\" is mapped to\nthe character `=` (equal sign), which represents \"null\", or, the end\nof meaningful characters in the sequence. Hence, everytime that the algorithm produces a\n\"padding group\", this group is automatically mapped to `=`.\n\nAs another example, if you give the string \"0\" as input to a base64 encoder, this string is\ntranslated into the base64 sequence \"MA==\".\nThe character \"0\" is, in binary, the sequence `00110000`[^zero-note]. So, with the 6-bit transformation\nexposed at @fig-base64-algo1, this single character would produce these two 6-bit groups: `001100`, `000000`.\nThe remaining two 6-bit groups become \"padding groups\". That is why the last\ntwo characters in the output sequence (MA==) are `==`.\n\n\n[^zero-note]: Notice that, the character \"0\" is different than the actual number 0, which is simply zero in binary.\n\n### A base64 decoder {#sec-base64-decoder-algo}\n\nThe algorithm behind a base64 decoder is essentially the inverse process of a base64 encoder.\nA base64 decoder needs to translate base64 messages back into their original meaning,\ni.e. into the original sequence of binary data.\n\nA base64 decoder usually works on a window of 4 bytes. Because it wants to convert these 4 bytes\nback into the original sequence of 3 bytes, that was converted into 4 groups of 6 bits by the\nbase64 encoder. Remember, in a base64 decoder we are essentially reverting the process made\nby the base64 encoder.\n\nEach byte in the input string (the base64 encoded string) normally contributes to re-create\ntwo different bytes in the output (the original binary data).\nIn other words, each byte that comes out of a base64 decoder is created by transforming merging two different\nbytes in the input together. You can visualize this relationship at @fig-base64-algo2:\n\n{#fig-base64-algo2}\n\nThe exact transformations, or, the exact steps applied to each byte from the input to transform them\ninto the bytes of the output, are a bit tricky to visualize in a figure like this. Because of that, I have\nsummarized these transformations as \"Some bit shifting and additions ...\" in the figure. These transformations\nwill be described in depth later.\n\nBesides that, if you look again at @fig-base64-algo2, you will notice that the character `=` was completely\nignored by the algorithm. Remember, this is just a special character that marks the end of meaningful characters\nin the base64 sequence. So, every `=` character in a base64 encoded sequence should be ignored by a base64 decoder.\n\n\n## Difference between encode and decode {#sec-encode-vs-decode}\n\nIf you don't have any previous experience with base64, you might not understand the differences\nbetween \"encode\" and \"decode\". Essentially, the terms \"encode\" and \"decode\" here\nhave the exact same meaning as they have in the field of encryption (i.e. they mean the same thing as \"encode\" and \"decode\" in hashing\nalgorithms, like the MD5 algorithm).\n\nThus, \"encode\" means that we want to encode, or, in other words, we want to translate some message into\nthe base64 encoding system. We want to produce the sequence of base64 characters that represent this\noriginal message in the base64 encoding system.\n\nIn contrast, \"decode\" represents the inverse process.\nWe want to decode, or, in other words, translate a base64 message back to its original content.\nSo, in this process we get a sequence of base64 characters as input, and produce as output,\nthe binary data that is represented by this sequence of base64 characters.\n\nAny base64 library is normally composed of these two parts: 1) the encoder, which is a function that encodes\n(i.e. it converts) any sequence of binary data into a sequence of base64 characters; 2) the decoder, which is a function\nthat converts a sequence of base64 characters back into the original sequence of binary data.\n\n\n\n## Calculating the size of the output {#sec-base64-length-out}\n\nOne task that we need to do is to calculate how much space we need to reserve for the\noutput, both of the encoder and decoder. This is simple math, and can be done easily in Zig\nbecause every array has its length (its number of elements) easily accesible by consulting\nthe `.len` property of the array.\n\nFor the encoder, the logic is the following: for each 3 bytes that we find in the input,\n4 new bytes are created in the output. So, we take the number of bytes in the input, divide it\nby 3, use a ceiling function, then, we multiply the result by 4. That way, we get the total\nnumber of bytes that will be produced by the encoder in its output.\n\nThe `_calc_encode_length()` function below encapsulates this logic.\nInside this function, we take the length of the input array,\nwe divide it by 3, and apply a ceil operation over the result by using the\n`divCeil()` function from the Zig Standard Library. Lastly, we multiply\nthe end result by 4 to get the answer we need.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn _calc_encode_length(input: []const u8) !usize {\n if (input.len < 3) {\n const n_output: usize = 4;\n return n_output;\n }\n const n_output: usize = try std.math.divCeil(\n usize, input.len, 3\n );\n return n_output * 4;\n}\n```\n:::\n\n\n\n\n\nAlso, you might have notice that, if the input length is less than 3 bytes, then, the output length of the encoder is\nalways 4 bytes. This is the case for every input with less than 3 bytes, because, as I described at @sec-base64-encoder-algo,\nthe algorithm always produces enough \"padding-groups\" in the end result, to complete the 4 bytes window.\n\nNow, for the decoder, we just need to apply the inverse logic: for each 4 bytes in the input, 3 bytes\nwill be produced in the output of the decoder. I mean, this is roughly true, because we also need to\ntake the `=` character into account, which is always ignored by the decoder, as we described at @sec-base64-decoder-algo, and,\nat @fig-base64-algo2. But we can ignore this fact for now, just to keep things simple.\n\nThe function `_calc_decode_length()` exposed below summarizes this logic that we described. It is very similar\nto the function `_calc_encode_length()`. Only the division part is twisted, and also, in the special\ncase where we have less than 4 bytes in the input to work on. Also notice that this time, we apply\na floor operation over the output of the division, by using the `divFloor()`\nfunction (instead of a ceiling operation with `divCeil()`).\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nfn _calc_decode_length(input: []const u8) !usize {\n if (input.len < 4) {\n const n_output: usize = 3;\n return n_output;\n }\n const n_output: usize = try std.math.divFloor(\n usize, input.len, 4\n );\n return n_output * 3;\n}\n```\n:::\n\n\n\n\n\n## Building the encoder logic {#sec-encoder-logic}\n\nIn this section, we can start building the logic behind the `encode()` function, which\nwill be responsible for encoding messages into the base64 encoding system.\nIf you are an anxious person, and you want to see now the full source code of the implementation\nfor this base64 encoder/decoder, you can find it at the `ZigExamples` folder in the official repository of\nthis book[^zig-base64-algo].\n\n[^zig-base64-algo]: .\n\n\n\n### The 6-bit transformation {#sec-6bit-transf}\n\nThe 6-bit transformation presented at @fig-base64-algo1 is the core part of the base64 encoder algorithm.\nBy understanding how this transformation is made in code, the rest of the algorithm becomes much simpler\nto comprehend.\n\nIn essence, this 6-bit transformation is made with the help of bitwise operators.\nBitwise operators are essential to any type of low-level operation that is done at the bit-level. For the specific case of the base64 algorithm,\nthe operators *bif shift to the left* (`<<`), *bit shift to the right* (`>>`), and the *bitwise and* (`&`) are used. They\nare the core solution for the 6-bit transformation.\n\nThere are 3 different scenarios that we need to take into account in this transformation. First, is the perfect scenario,\nwhere we have the perfect window of 3 bytes to work on. Second, we have the scenario where we have a window of only\ntwo bytes to work with. And last, we have the scenario where we have a window of one single byte.\n\nIn each of these 3 scenarios, the 6-bit transformation works a bit differently. To make the explanation\neasier, I will use the variable `output` to refer to the bytes in the output of the base64 encoder,\nand the variable `input` to refer to the bytes in the input of the encoder.\n\n\nSo, if you have the perfect window of 3 bytes, these are steps for the 6-bit transformation:\n\n1. `output[0]` is produced by moving the bits from `input[0]` two positions to the right.\n1. `output[1]` is produced by summing two components. First, take the last two bits from `input[0]`, then, move them four positions to the left. Second, move the bits from `input[1]` four positions to the right. Sum these two components.\n1. `output[2]` is produced by summing two components. First, take the last four bits from `input[1]`, then, move them two positions to the left. Second, move the bits from `input[2]` six positions to the right. Sum these two components.\n1. `output[3]` is produced by taking the last six bits from `input[2]`.\n\n\nThis is the perfect scenario, where we have a full window of 3 bytes to work on.\nJust to make things as clear as possible, the @fig-encoder-bitshift demonstrates visually how\nthe step 2 mentioned above works. So the 2nd byte in the `output` of the encoder, is made by taking the 1st byte (dark purple)\nand the 2nd byte (orange) from the input. You can see that, at the end of the process, we get a new\nbyte that contains the last 2 bits from the 1st byte in the `input`, and the first 4 bits\nfrom the 2nd byte in the `input`.\n\n{#fig-encoder-bitshift}\n\nOn the other hand, we must be prepared for the instances where we do not have the perfect window of 3 bytes.\nIf you have a window of 2 bytes, then, the steps 3 and 4, which produces the bytes `output[2]` and `output[3]`, change a little bit,\nand they become:\n\n- `output[2]` is produced by taking the last 4 bits from `input[1]`, then, move them two positions to the left.\n- `output[3]` is the character `'='`.\n\n\nFinally, if you have a window of a single byte, then, the steps 2 to 4, which produces the bytes `output[1]`, `output[2]` and `output[3]` change,\nbecoming:\n\n- `output[1]` is produced by taking the last two bits from `input[0]`, then, move them four positions to the left.\n- `output[2]` and `output[3]` are the character `=`.\n\n\nIf these bullet points were a bit confusing for you, you may find the @tbl-transf-6bit more intuitive.\nThis table unifies all this logic into a simple table. Notice that\nthis table also provides the exact expression in Zig that creates the corresponding\nbyte in the output.\n\n\n::: {#tbl-transf-6bit}\n\n| Number of bytes in the window | Byte index in the output | In code |\n|-------------------------------|--------------------------|--------------------------------------------|\n| 3 | 0 | input[0] >> 2 |\n| 3 | 1 | ((input[0] & 0x03) << 4) + (input[1] >> 4) |\n| 3 | 2 | ((input[1] & 0x0f) << 2) + (input[2] >> 6) |\n| 3 | 3 | input[2] & 0x3f |\n| 2 | 0 | input[0] >> 2 |\n| 2 | 1 | ((input[0] & 0x03) << 4) + (input[1] >> 4) |\n| 2 | 2 | ((input[1] & 0x0f) << 2) |\n| 2 | 3 | '=' |\n| 1 | 0 | input[0] >> 2 |\n| 1 | 1 | ((input[0] & 0x03) << 4) |\n| 1 | 2 | '=' |\n| 1 | 3 | '=' |\n\n: How the 6-bit transformation translates into code in different window settings.\n\n:::\n\n\n\n\n\n\n### Bit-shifting in Zig\n\nBit-shifting in Zig works similarly to bit-shifting in C.\nAll bitwise operators that exist in C are available in Zig.\nHere, in the base64 encoder algorithm, they are essential\nto produce the result we want.\n\nFor those who are not familiar with these operators, they are\noperators that operates at the bit-level of your values.\nThis means that these operators takes the bits that form the value\nyou have, and change them in some way. This ultimately also changes\nthe value itself, because the binary representation of this value\nchanges.\n\nWe have already seen at @fig-encoder-bitshift the effect produced by a bit-shift.\nBut let's use the first byte in the output of the base64 encoder as another example of what\nbit-shifting means. This is the easiest byte of the 4 bytes in the output\nto build. Because we only need to move the bits from the first byte in the input two positions to the right,\nwith the *bit shift to the right* (`>>`) operator.\n\nIf we take the string \"Hi\" that we used at @fig-base64-algo1 as an example, the first byte in\nthis string is \"H\", which is the sequence `01001000` in binary.\nIf we move the bits of this byte, two places to the right, we get the sequence `00010010` as result.\nThis binary sequence is the value `18` in decimal, and also, the value `0x12` in hexadecimal.\nNotice that the first 6 bits of \"H\" were moved to the end of the byte.\nWith this operation, we get the first byte of the output.\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 input = \"Hi\";\n try stdout.print(\"{d}\\n\", .{input[0] >> 2});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n18\n```\n\n\n:::\n:::\n\n\n\n\nIf you recall @fig-base64-algo1, the first byte present in the output should\nbe equivalent to the 6-bit group `010010`. Although being visually different, the\nsequences `010010` and `00010010` are semantically equal. They mean the same thing.\nThey both represent the number 18 in decimal, and the value `0x12` in hexadecimal.\n\nSo, don't take the \"6-bit group\" factor so seriously. We do not need necessarily to\nget a 6-bit sequence as result. As long as the meaning of the 8-bit sequence we get is the same\nof the 6-bit sequence, we are in the clear.\n\n\n\n### Selecting specific bits with the `&` operator\n\nIf you comeback to @sec-6bit-transf, you will see that, in order to produce\nthe second and third bytes in the output, we need to select specific\nbits from the first and second bytes in the input string. But how\ncan we do that? The answer relies on the *bitwise and* (`&`) operator.\n\nThe @fig-encoder-bitshift already showed you what effect this `&` operator\nproduces in the bits of its operands. But let's make a clear description of it.\n\nIn summary, the `&` operator performs a logical conjunction operation\nbetween the bits of its operands. In more details, the operator `&`\ncompares each bit of the first operand to the corresponding bit of the second operand.\nIf both bits are 1, the corresponding result bit is set to 1.\nOtherwise, the corresponding result bit is set to 0 [@microsoftbitwiseand].\n\nSo, if we apply this operator to the binary sequences `1000100` and `00001101`\nthe result of this operation is the binary sequence `00000100`. Because only\nat the sixth position in both binary sequences we had a 1 value. So any\nposition where we do not have both binary sequences setted to 1, we get\na 0 bit in the resulting binary sequence.\n\nWe lose information about the original bit values\nfrom both sequences in this case. Because we no longer know\nif this 0 bit in the resulting binary sequence was produced by\ncombining 0 with 0, or 1 with 0, or 0 with 1.\n\nAs an example, suppose you have the binary sequence `10010111`, which is the number 151 in decimal. How\ncan we get a new binary sequence which contains only the third and\nfourth bits of this sequence?\n\nWe just need to combine this sequence with `00110000` (is `0x30` in hexadecimal) using the `&` operator.\nNotice that only the third and fourth positions in this binary sequence is setted to 1. As a consequence, only the\nthird and fourth values of both binary sequences are potentially preserved in the output. All the remaining positions\nare setted to zero in the output sequence, which is `00010000` (is the number 16 in decimal).\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 bits = 0b10010111;\n try stdout.print(\"{d}\\n\", .{bits & 0b00110000});\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\n16\n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Allocating space for the output\n\nAs I described at @sec-stack, to store an object in the stack,\nthis object needs to have a known and fixed length at compile-time. This is an important\nlimitation for our base64 encoder/decoder case. Because the size of\nthe output (from both the encoder and decoder) depends\ndirectly on the size of the input.\n\nHaving this in mind, we cannot know at compile time which is\nthe size of the output for both the encoder and decoder.\nSo, if we can't know the size of the output at compile time,\nthis means that we cannot store the output for both the encoder\nand decoder in the stack.\n\nConsequently, we need to store this output on the heap,\nand, as I commented at @sec-heap, we can only\nstore objects in the heap by using allocator objects.\nSo, one the arguments to both the `encode()` and `decode()`\nfunctions, needs to be an allocator object, because\nwe know for sure that, at some point inside the body of these\nfunctions, we need to allocate space on the heap to\nstore the output of these functions.\n\nThat is why, both the `encode()` and `decode()` functions that I\npresent in this book, have an argument called `allocator`,\nwhich receives a allocator object as input, identified by\nthe type `std.mem.Allocator` from the Zig Standard Library.\n\n\n\n### Writing the `encode()` function\n\nNow that we have a basic understanding on how the bitwise operators work, and how\nexactly they help us to achieve the result we want to achieve. We can now encapsulate\nall the logic that we have described at @fig-base64-algo1 and @tbl-transf-6bit into a nice\nfunction that we can add to our `Base64` struct definition, that we started at @sec-base64-table.\n\nYou can find the `encode()` function below. Notice that the first argument of this function,\nis the `Base64` struct itself. Therefore, this argument clearly signals\nthat this function is a method from the `Base64` struct.\n\nBecause the `encode()` function itself is fairly long,\nI intentionally omitted the `Base64` struct definition in this source code,\njust for brevity reasons. So, just remember that this function is a public function (or a public method) from the\n`Base64` struct.\n\nFurthermore, this `encode()` function has two other arguments:\n\n1. `input` is the input sequence of characters that you want to encode in base64;\n1. `allocator` is an allocator object to use in the necessary memory allocations.\n\nI described everything you need to know about allocator objects at @sec-allocators.\nSo, if you are not familiar with them, I highly recommend you to comeback to\nthat section, and read it.\nBy looking at the `encode()` function, you will see that we use this\nallocator object to allocate enough memory to store the output of\nthe encoding process.\n\nThe main for loop in the function is responsible for iterating through the entire input string.\nIn every iteration, we use a `count` variable to count how many iterations we had at the\nmoment. When `count` reaches 3, then, we try to encode the 3 characters (or bytes) that we have accumulated\nin the temporary buffer object (`buf`).\n\nAfter encoding these 3 characters and storing the result in the `output` variable, we reset\nthe `count` variable to zero, and start to count again on the next iteration of the loop.\nIf the loop hits the end of the string, and, the `count` variable is less than 3, then, it means that\nthe temporary buffer contains the last 1 or 2 bytes from the input.\nThat is why we have two `if` statements after the for loop. To deal which each possible case.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\npub fn encode(self: Base64,\n allocator: std.mem.Allocator,\n input: []const u8) ![]u8 {\n\n if (input.len == 0) {\n return \"\";\n }\n\n const n_out = try _calc_encode_length(input);\n var out = try allocator.alloc(u8, n_out);\n var buf = [3]u8{ 0, 0, 0 };\n var count: u8 = 0;\n var iout: u64 = 0;\n\n for (input, 0..) |_, i| {\n buf[count] = input[i];\n count += 1;\n if (count == 3) {\n out[iout] = self._char_at(buf[0] >> 2);\n out[iout + 1] = self._char_at(\n ((buf[0] & 0x03) << 4) + (buf[1] >> 4)\n );\n out[iout + 2] = self._char_at(\n ((buf[1] & 0x0f) << 2) + (buf[2] >> 6)\n );\n out[iout + 3] = self._char_at(buf[2] & 0x3f);\n iout += 4;\n count = 0;\n }\n }\n\n if (count == 1) {\n out[iout] = self._char_at(buf[0] >> 2);\n out[iout + 1] = self._char_at(\n (buf[0] & 0x03) << 4\n );\n out[iout + 2] = '=';\n out[iout + 3] = '=';\n }\n\n if (count == 2) {\n out[iout] = self._char_at(buf[0] >> 2);\n out[iout + 1] = self._char_at(\n ((buf[0] & 0x03) << 4) + (buf[1] >> 4)\n );\n out[iout + 2] = self._char_at(\n (buf[1] & 0x0f) << 2\n );\n out[iout + 3] = '=';\n iout += 4;\n }\n\n return out;\n}\n```\n:::\n\n\n\n\n\n\n## Building the decoder logic {#sec-decoder-logic}\n\nNow, we can focus on writing the base64 decoder logic. Remember from @fig-base64-algo2 that,\na base64 decoder does the inverse process of an encoder. So, all we need to do, is to\nwrite a `decode()` function that performs the inverse process that I exposed at @sec-encoder-logic.\n\n\n### Mapping base64 characters to their indexes {#sec-map-base64-index}\n\nOne thing that we need to do, in order to decode a base64-encoded message, is to calculate\nthe index in the base64 scale of every base64 character that we encounter in the decoder input.\n\nIn other words, the decoder receives as input, a sequence of base64 characters. We need\nto translate this sequence of characters into a sequence of indexes. These indexes\nare the index of each character in the base64 scale. This way, we get the value/byte\nthat was calculated in the 6-bit transformation step of the encoder process.\n\nThere are probably better/faster ways to calculate this, especially using a \"divide and conquer\"\ntype of strategy. But for now, I am satisfied with a simple and \"brute force\" type of strategy.\nThe `_char_index()` function below contains this strategy.\n\nWe are essentially looping through the *lookup table* with the base64 scale,\nand comparing the character we got with each character in the base64 scale.\nIf these characters match, then, we return the index of this character in the\nbase64 scale as the result.\n\nNotice that, if the input character is `'='`, the function returns the index 64, which is\n\"out of range\" in the scale. But, as I described at @sec-base64-scale,\nthe character `'='` does not belong to the base64 scale itself.\nIt is a special and meaningless character in base64.\n\nAlso notice that this `_char_index()` function is a method from our `Base64` struct,\nbecause of the `self` argument. Again, I have omitted the `Base64` struct definition in this example\nfor brevity reasons.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfn _char_index(self: Base64, char: u8) u8 {\n if (char == '=')\n return 64;\n var index: u8 = 0;\n for (0..63) |i| {\n if (self._char_at(i) == char)\n break;\n index += 1;\n }\n\n return index;\n}\n```\n:::\n\n\n\n\n\n\n### The 6-bit transformation\n\nOnce again, the core part of the algorithm is the 6-bit transformation.\nIf we understand the necessary steps to perform this transformation, the rest\nof the algorithm becomes much easier.\n\nFirst of all, before we actually go to the 6-bit transformation,\nwe need to make sure that we use `_char_index()` to convert the sequence of base64 characters\ninto a sequence of indexes. So the snippet below is important for the job that will be done.\nThe result of `_char_index()` is stored in a temporary buffer, and this temporary buffer\nis what we are going to use in the 6-bit transformation, instead of the actual `input` object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfor (0..input.len) |i| {\n buf[i] = self._char_index(input[i]);\n}\n```\n:::\n\n\n\n\nNow, instead of producing 4 bytes (or 4 characters) as output per each window of 3 characters in the input,\na base64 decoder produces 3 bytes (or 3 characters) as output per each window of 4 characters in the input.\nOnce again, is the inverse process.\n\nSo, the steps to produce the 3 bytes in the output are:\n\n1. `output[0]` is produced by summing two components. First, move the bits from `buf[0]` two positions to the left. Second, move the bits from `buf[1]` 4 positions to the right. Then, sum these two components.\n1. `output[1]` is produced by summing two components. First, move the bits from `buf[1]` four positions to the left. Second, move the bits from `buf[2]` 2 positions to the right. Then, sum these two components.\n1. `output[2]` is produced by summing two components. First, move the bits from `buf[2]` six positions to the left. Then, you sum the result with `buf[3]`.\n\n\nBefore we continue, let's try to visualize how these transformations make the original bytes that we had\nbefore the encoding process. First, think back to the 6-bit transformation performed by the encoder exposed at @sec-encoder-logic.\nThe first byte in the output of the encoder is produced by moving the bits in the first byte of the input two positions to the right.\n\nIf for example the first byte in the input of the encoder was the sequence `ABCDEFGH`, then, the first byte in the output of the encoder would be\n`00ABCDEF` (this sequence would be the first byte in the input of the decoder). Now, if the second byte in the input of the encoder was the sequence\n`IJKLMNOP`, then, the second byte in the encoder output would be `00GHIJKL` (as we demonstrated at @fig-encoder-bitshift).\n\nHence, if the sequences `00ABCDEF` and `00GHIJKL` are the first and second bytes, respectively, in the input of the decoder, the\n@fig-decoder-bitshift demonstrates visually how these two bytes are transformed into the first byte of the output of the decoder.\nNotice that the output byte is the sequence `ABCDEFGH`, which is the original byte from the input of the encoder.\n\n{#fig-decoder-bitshift}\n\nThe @tbl-6bit-decode presents how the three steps described earlier translate into Zig code:\n\n\n\n::: {#tbl-6bit-decode}\n\n| Byte index in the output | In code |\n|--------------------------|-------------------------------|\n| 0 | (buf[0] << 2) + (buf[1] >> 4) |\n| 1 | (buf[1] << 4) + (buf[2] >> 2) |\n| 2 | (buf[2] << 6) + buf[3] |\n\n: The necessary steps for the 6-transformation in the decode process.\n\n\n:::\n\n\n\n\n\n\n\n### Writing the `decode()` function\n\nThe `decode()` function below contains the entire decoding process.\nWe first calculate the size of the output, with\n`_calc_decode_length()`, then, we allocate enough memory for this output with\nthe allocator object.\n\nThree temporary variables are created: 1) `count`, to hold the window count\nin each iteration of the for loop; 2) `iout`, to hold the current index in the output;\n3) `buf`, which is the temporary buffer that holds the base64 indexes to be\nconverted through the 6-bit transformation.\n\nThen, in each iteration of the for loop we fill the temporary buffer with the current\nwindow of bytes. When `count` hits the number 4, then, we have a full window of\nindexes in `buf` to be converted, and then, we apply the 6-bit transformation\nover the temporary buffer.\n\nNotice that we check if the indexes 2 and 3 in the temporary buffer are the number 64, which, if you recall\nfrom @sec-map-base64-index, is when the `_calc_index()` function receives a `'='` character\nas input. So, if these indexes are equal to the number 64, the `decode()` function knows\nthat it can simply ignore these indexes. They are not converted because, as I described before,\nthe character `'='` has no meaning, despite being the end of meaningful characters in the sequence.\nSo we can safely ignore them when they appear in the sequence.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nfn decode(self: Base64,\n allocator: std.mem.Allocator,\n input: []const u8) ![]u8 {\n\n if (input.len == 0) {\n return \"\";\n }\n const n_output = try _calc_decode_length(input);\n var output = try allocator.alloc(u8, n_output);\n var count: u8 = 0;\n var iout: u64 = 0;\n var buf = [4]u8{ 0, 0, 0, 0 };\n\n for (0..input.len) |i| {\n buf[count] = self._char_index(input[i]);\n count += 1;\n if (count == 4) {\n output[iout] = (buf[0] << 2) + (buf[1] >> 4);\n if (buf[2] != 64) {\n output[iout + 1] = (buf[1] << 4) + (buf[2] >> 2);\n }\n if (buf[3] != 64) {\n output[iout + 2] = (buf[2] << 6) + buf[3];\n }\n iout += 3;\n count = 0;\n }\n }\n\n return output;\n}\n```\n:::\n\n\n\n\n\n## The end result\n\nNow that we have both `decode()` and `encode()` implemented. We have a fully functioning\nbase64 encoder/decoder implemented in Zig. Here is an usage example of our\n`Base64` struct with the `encode()` and `decode()` methods that we have implemented.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nvar memory_buffer: [1000]u8 = undefined;\nvar fba = std.heap.FixedBufferAllocator.init(\n &memory_buffer\n);\nconst allocator = fba.allocator();\n\nconst text = \"Testing some more stuff\";\nconst etext = \"VGVzdGluZyBzb21lIG1vcmUgc3R1ZmY=\";\nconst base64 = Base64.init();\nconst encoded_text = try base64.encode(\n allocator, text\n);\nconst decoded_text = try base64.decode(\n allocator, etext\n);\ntry stdout.print(\n \"Encoded text: {s}\\n\", .{encoded_text}\n);\ntry stdout.print(\n \"Decoded text: {s}\\n\", .{decoded_text}\n);\n```\n:::\n\n\n\n\n```\nEncoded text: VGVzdGluZyBzb21lIG1vcmUgc3R1ZmY=\nDecoded text: Testing some more stuff\n```\n\nYou can also see the full source code at once, by visiting the official repository of this book[^repo].\nMore precisely inside the `ZigExamples` folder[^zig-base64-algo].\n\n[^repo]: \n",
"supporting": [
"01-base64_files"
],
diff --git a/_freeze/Chapters/01-zig-weird/execute-results/html.json b/_freeze/Chapters/01-zig-weird/execute-results/html.json
index 1238c4cd..f1342fff 100644
--- a/_freeze/Chapters/01-zig-weird/execute-results/html.json
+++ b/_freeze/Chapters/01-zig-weird/execute-results/html.json
@@ -1,9 +1,11 @@
{
- "hash": "8ffa4a01e784b723e1aaf1806367c53c",
+ "hash": "ba740c33772422f50c73c140fef90a2d",
"result": {
"engine": "knitr",
- "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"../Assets/zig.xml\"\n---\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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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\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\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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\n\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\n\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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 portable 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\nIn 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOS. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \n\n\n### Compiling the entire project {#sec-compile-project}\n\nJust as I described in @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 does 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 later 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 can you 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 to 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 documentation.\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://ziglings.org)[^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 (on 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 specify 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 compile successfully. 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\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\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 successfully.\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = 24;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar age: u8 = undefined;\nage = 25;\n```\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst age = 15;\n```\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;\n1. 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 a 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 successfully compiled our source code.\n\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\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\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nvar where_i_live = \"Belo Horizonte\";\n_ = where_i_live;\n```\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 objects 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\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\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 common 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 {#sec-select-array-elem}\n\nOne very common 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\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\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\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\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\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\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\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\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 accompanied 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\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\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\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\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\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\n### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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\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\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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\n\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\n\nZig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\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\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\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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. As an example, the `string_object` object below is 43 bytes long:\n\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\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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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\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.write(\n \"Bytes that represents the string object: \"\n );\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\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\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
- "supporting": [],
+ "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, its 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, it will be 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 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 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e.\nthey obscure 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 also don't have a 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 `init` 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\n\nLow-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\nIn Zig, we don't need to use a separate set of tools to build our projects,\nbecause a build system is embedded inside the language itself.\nTherefore, 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 a JSON-like file, in which you can describe\nyour project, and also, declare a set of dependencies of your project that you want to fetch from the internet.\nIn other words, you can use this `build.zig.zon` file to include a list of external libraries in your project.\n\nOne possible way to include an external Zig library in your project, is to manually build\nand install the library in your system, and just link your source code\nwith the library at the build step of your project.\n\nHowever, if this external Zig library is available on GitHub for example,\nand it has a valid `build.zig.zon` file in root folder of the project,\nwhich describes the project, you can easily include this library in\nyour project by simply listing this external library in your `build.zig.zon` file.\n\nIn other words, this `build.zig.zon` file works similarly to the `package.json`\nfile in Javascript projects, or the `Pipfile` file in Python projects,\nor the `Cargo.toml` file in Rust projects. You can read more about this\nspecific file in a couple of articles on the internet[^zig-zon][^zig-zon2], and\nyou can also see the expected schema for this `build.zig.zon` file\nin a documentation file inside the official repository of Zig[^zig-zon-schema].\n\n[^zig-zon]: \n[^zig-zon2]: \n[^zig-zon-schema]: \n\n### The file `root.zig` {#sec-root-file}\n\nLet's take a look into the `root.zig` file.\nYou might have noticed 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.\nThis `@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 a strongly-typed language. There are some specific situations where you can (if you want to) omit\nthe type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more\nabout that at @sec-type-inference). But there are other situations where you do need to be explicit.\nFor example, you do have to explicitly specify the type of each function argument, and also,\nthe return type of every function that you create in Zig.\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. Therefore, if you are writing\na library for other people to use, you have to expose the functions\nyou write in the public API of this library by using this `export` keyword.\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\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 there are some other elements that we haven't 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. The return type of the function (`void`) is accompanied by an exclamation mark (`!`).\nThis exclamation mark tells us that this `main()` function might return an error.\n\nIs worth noting that, a `main()` function in Zig is allowed to return nothing (`void`),\nor an unsigned 8-bit integer (`u8`) value[^u8-example], or an error. In other words, you can write your `main()` function in Zig\nto return essentially nothing (`void`), or, if you prefer, you can also write a more C-like `main()` function,\nwhich returns an integer value that usually serves as a \"status code\" for the process.\n\n[^u8-example]: You can see an example of a `main()` function that returns an `u8` value in the `return-integer.zig` file, \n\nIn this example, the return type annotation of `main()` indicates that this function can either\nreturn nothing (`void`), or return an error. This exclamation mark in the return type annotation\nis an interesting and powerful feature of Zig. In summary, if you write a function and something inside\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 and make it clear that\nthis function might return an error.\n- explicitly handle this error inside the function.\n\nIn most programming languages, we normally handle (or deal with) an error through\na *try catch* pattern. Zig does have both `try` and `catch` keywords. But they work\na little differently than what you're probably used to in other languages.\n\nIf we look at the `main()` function below, you can see that we do have a `try` keyword\non the 5th line. But we do not have a `catch` keyword in this code.\nIn Zig, we use the `try` keyword to execute an expression that might return an error,\nwhich, in this example, is the `stdout.print()` expression.\n\nIn essence, the `try` keyword executes the expression `stdout.print()`. If this expression\nreturns a valid value, then, the `try` keyword does absolutely nothing. It only passes the value forward.\nIt is like if this `try` keyword was never there. However, if the expression does return an error, then,\nthe `try` keyword will unwrap the error value, then, it returns this error from the function\nand also prints the current stack 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 stop even if you don't want\nto stop the execution. You are obligated to face the error.\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\nAnother thing that you might have noticed in this code example, is that\nthe `main()` function is marked with the `pub` keyword. It marks the `main()`\nfunction as a *public function* from this module. Every function in your Zig\nmodule is by default private to this Zig module and can only be called from within the module.\nUnless, you explicitly mark this function as a public function with the `pub` keyword.\n\nIf you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword\ndo in C/C++. By making a function \"public\" you allow other Zig modules to access and call this function.\nA calling Zig module imports another module by using the `@import()` built-in function, which makes\nall public functions from the imported module visible to the calling 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 portable 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\nIn 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\n### Important note for Windows users\n\nFirst of all, this is a Windows-specific thing, and, therefore, does not apply to other\noperating systems, such as Linux and macOS. In summary, if you have a piece of Zig code that\nincludes some global variables whose initialization rely on runtime resources, then,\nyou might have some troubles while trying to compile this Zig code on Windows.\n\nAn example of that is accessing the `stdout` (i.e. the *standard output* of your system), which is usually\ndone in Zig by using the expression `std.io.getStdOut()`. Thus, if you use this expression to instantiate\na global variable in a Zig module, then, the compilation of your Zig code will very likely fail on Windows,\nwith a \"unable to evaluate comptime expression\" error message.\n\nThis failure in the compilation process happens because all global variables in Zig are initialized\nat *compile-time*. However, operations like accessing the `stdout` (or opening a file) on Windows\ndepends on resources that are available only at *runtime* (you will learn more about compile-time\nversus runtime at @sec-compile-time).\n\nFor example, if you try to compile this code example on Windows, you will likely get the error message\nexposed below:\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\n// ERROR! Compile-time error that emerge from\n// this next line, on the `stdout` object\nconst stdout = std.io.getStdOut().writer();\n\npub fn main() !void {\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nt.zig:2107:28: error: unable to evaluate comptime expression\n break :blk asm {\n ^~~\n```\n\n\n\nTo avoid this problem on Windows, we need to force the `zig` compiler to instantiate this\n`stdout` object only at runtime, instead of instantiating it at compile-time. We can achieve\nthat by simply moving the expression to a function body.\n\nThis solves the problem because all expressions that are inside a function body in Zig\nare evaluated only at runtime, unless you use the `comptime` keyword explicitly to change this behaviour.\nYou will learn more about this `comptime` keyword at @sec-comptime.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n // SUCCESS: Stdout initialized at runtime.\n const stdout = std.io.getStdOut().writer();\n _ = try stdout.write(\"Hello\\n\");\n}\n```\n:::\n\n\n\n\n```\nHello\n```\n\nYou can read more details about this Windows-specific limitation at a couple of\nGitHub issues opened at the official Zig repository. More specifically, the issues \n17186 [^cissue1] and 19864 [^cissue2].\n\n[^cissue1]: \n[^cissue2]: \n\n\n### Compiling the entire project {#sec-compile-project}\n\nJust as I described in @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 does 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 later 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 can you 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 to 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 documentation.\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://ziglings.org)[^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 (on 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 specify 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 compile successfully. 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 successfully.\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;\n1. 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 a 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 successfully 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 objects 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 common 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 {#sec-select-array-elem}\n\nOne very common 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 accompanied 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### Runtime versus compile-time known length in slices\n\nWe are going to talk a lot about the differences between compile-time known\nand runtime known across this book, especially at @sec-compile-time.\nBut the basic idea is that a thing is compile-time known, when we know\neverything (the value, the attributes and the characteristics) about this thing at compile-time.\nIn contrast, a runtime known thing is when the exact value of a thing is calculated only at runtime.\nTherefore, we don't know the value of this thing at compile-time, only at runtime.\n\nWe have learned at @sec-select-array-elem that slices are created by using a *range selector*,\nwhich represents a range of indexes. When this \"range of indexes\" (i.e. the start and the end of this range)\nis known at compile-time, the slice object that get's created is actually, under the hood, just\na single-item pointer to an array.\n\nYou don't need to precisely understand what that means now. We are going to talk a lot about pointers\nat @sec-pointer. For now, just understand that, when the range of indexes is known at compile-time,\nthe slice that get's created is just a pointer to an array, accompanied by a length value that\ntells the size of the slice.\n\nIf you have a slice object like this, i.e. a slice that has a compile-time known range,\nyou can use common pointer operations over this slice object. For example, you can \ndereference the pointer of this slice, by using the `.*` method, like you would\ndo on a normal pointer object.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst arr1 = [10]u64 {\n 1, 2, 3, 4, 5,\n 6, 7, 8, 9, 10\n};\n// This slice have a compile-time known range.\n// Because we know both the start and end of the range.\nconst slice = arr1[1..4];\n```\n:::\n\n\n\n\n\nOn the other hand, if the range of indexes is not known at compile time, then, the slice object\nthat get's created is not a pointer anymore, and, thus, it does not support pointer operations.\nFor example, maybe the start index is known at compile time, but the end index is not. In such\ncase, the range of the slice becomes runtime known only.\n\nIn the example below, the `slice` object have a runtime known range, because the end index of the range\nis not known at compile time. In other words, the size of the array at `buffer` is not known\nat compile time. When we execute this program, the size of the array might be 10, or, it might be 12\ndepending on where we execute it. Therefore, we don't know at compile time if\nthe slice object have a range of size 10, or, a range of size 12.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub fn main() !void {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const allocator = gpa.allocator();\n var n: usize = 0;\n if (builtin.target.os.tag == .windows) {\n n = 10;\n } else {\n n = 12;\n }\n const buffer = try allocator.alloc(u64, n);\n const slice = buffer[0..];\n _ = slice;\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 its 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 its 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 summary, there are two types of string values that you care about in Zig, which are:\n\n- String literal values.\n- String objects.\n\nA string literal value is just a pointer to a null-terminated array of bytes (i.e. similar to a C string).\nBut in Zig, a string literal value also embeds the length of the string into the data type of the value itself.\nTherefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type\nindicates the size of the string.\n\nOn the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes,\nor, in other words, a slice of `u8` values (slices were presented at @sec-arrays). Thus,\na string object have a data type of `[]u8` or `[]const u8`, depending if the string object is\nmarked as constant with `const`, or as variable with `var`.\n\nBecause a string object is essentially a slice, it means that a string object always contains two things:\na pointer to an array of bytes (i.e. `u8` values) that represents the string value; and also, a length value,\nwhich specifies the size of the slice, or, how many elements there is in the slice.\nIs worth to emphasize that the array of bytes in a string object is not null-terminated, like in a\nstring literal value.\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 the sequence of bytes in your string is UTF-8 encoded. This might not be true for every\nsequence of bytes you're working with, 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, especially on the web, should be UTF-8 encoded.\nSo if your string literal is not UTF-8 encoded, then, you will likely have 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 hexadecimal, 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\";\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 \n```\n\n\n:::\n:::\n\n\n\n\n\n### Strings in C\n\nAt first glance, a string literal value in Zig looks very similar to how C treats strings as well.\nIn more details, string values in C are treated internally as an array of arbitrary bytes,\nand this array is also null-terminated.\n\nBut one key difference between a Zig string literal and a C string, is that Zig also stores the length of\nthe string inside the object itself. In the case of a string literal value, this length is stored in the\ndata type of the value (i.e. the `n` variable in `[n:0]u8`). While, in a string object, the length is stored\nin the `len` attribute of the slice that represents the string object. This small detail makes your code safer,\nbecause it is much easier for the Zig compiler to check if you are trying to access an element that is\n\"out of bounds\", i.e. if your 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 throughout 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\n\n\n::: {.cell}\n\n```{.c .cell-code}\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\n\n\n\n```\nNumber of elements in the array: 25\n```\n\n\nYou don't have this kind of work in Zig. Because the length of the string is always\npresent and accessible. In a string object for example, you can easily access the length of the string\nthrough the `len` attribute. 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 an 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 literal value exposed below, you will find that this object is a\nconstant pointer (hence the `*const` annotation) to an array of 16 elements (or 16 bytes). Each element is a\nsingle byte (more precisely, an unsigned 8 bit integer - `u8`), that is why we have the `[16:0]u8` portion of the type below.\nIn other words, the string literal value exposed below is 16 bytes long.\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 literal value. This demonstrates that a string literal value\nin Zig is already a pointer to a null-terminated array of bytes.\n\nFurthermore, if we take a look at the type of the `string_obj` object, you will see that it is a\nslice object (hence the `[]` portion of the type) to a sequence of constant `u8` values (hence\nthe `const u8` portion of the type).\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst std = @import(\"std\");\npub fn main() !void {\n const simple_array = [_]i32{1, 2, 3, 4};\n const string_obj: []const u8 = \"A string object\";\n std.debug.print(\n \"Type 1: {}\\n\", .{@TypeOf(simple_array)}\n );\n std.debug.print(\n \"Type 2: {}\\n\", .{@TypeOf(\"A string literal\")}\n );\n std.debug.print(\n \"Type 3: {}\\n\", .{@TypeOf(&simple_array)}\n );\n std.debug.print(\n \"Type 4: {}\\n\", .{@TypeOf(string_obj)}\n );\n}\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\nType 1: [4]i32Type 2: *const [16:0]u8Type 3: *cons\n st [4]i32Type 4: []const u8\n```\n\n\n:::\n:::\n\n\n\n\n```\nType 1: [4]i32\nType 2: *const [16:0]u8\nType 3: *const [4]i32\nType 4: []const u8\n```\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.write(\n \"Bytes that represents the string object: \"\n );\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();\n\npub fn main() !void {\n var utf8 = try std.unicode.Utf8View.init(\"アメリカ\");\n var iterator = utf8.iterator();\n while (iterator.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\n```\ngot codepoint E382A2\ngot codepoint E383A1\ngot codepoint E383AA\ngot codepoint E382AB\n```\n\n\n### Some useful functions for strings {#sec-strings-useful-funs}\n\nIn this section, I just want to quickly describe some functions from the Zig Standard Library\nthat are very useful to use when working with strings. Most notably:\n\n- `std.mem.eql()`: to compare if two strings are equal.\n- `std.mem.splitScalar()`: to split a string into an array of substrings given a delimiter value.\n- `std.mem.splitSequence()`: to split a string into an array of substrings given a substring delimiter.\n- `std.mem.startsWith()`: to check if string starts with substring.\n- `std.mem.endsWith()`: to check if string ends with substring.\n- `std.mem.trim()`: to remove specific values from both start and end of the string.\n- `std.mem.concat()`: to concatenate strings together.\n- `std.mem.count()`: to count the occurrences of substring in the string.\n- `std.mem.replace()`: to replace the occurrences of substring in the string.\n\nNotice that all of these functions come from the `mem` module of\nthe Zig Standard Library. This module contains multiple functions and methods\nthat are useful to work with memory and sequences of bytes in general.\n\nThe `eql()` function is used to check if two arrays of data are equal or not.\nSince strings are just arbitrary arrays of bytes, we can use this function to compare two strings together.\nThis function returns a boolean value indicating if the two strings are equal\nor not. The first argument of this function is the data type of the elements of the arrays\nthat are being compared.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.eql(u8, name, \"Pedro\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `splitScalar()` and `splitSequence()` functions are useful to split\na string into multiple fragments, like the `split()` method from Python strings. The difference between these two\nmethods is that the `splitScalar()` uses a single character as the separator to\nsplit the string, while `splitSequence()` uses a sequence of characters (a.k.a. a substring)\nas the separator. There is a practical example of these functions later in the book.\n\nThe `startsWith()` and `endsWith()` functions are pretty straightforward. They\nreturn a boolean value indicating if the string (or, more precisely, if the array of data)\nbegins (`startsWith`) or ends (`endsWith`) with the sequence provided.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst name: []const u8 = \"Pedro\";\ntry stdout.print(\n \"{any}\\n\", .{std.mem.startsWith(u8, name, \"Pe\")}\n);\n```\n\n\n::: {.cell-output .cell-output-stdout}\n\n```\ntrue\n```\n\n\n:::\n:::\n\n\n\n\nThe `concat()` function, as the name suggests, concatenate two or more strings together.\nBecause the process of concatenating the strings involves allocating enough space to\naccomodate all the strings together, this `concat()` function receives an allocator\nobject as input.\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nconst str2 = \" you!\";\nconst str3 = try std.mem.concat(\n allocator, u8, &[_][]const u8{ str1, str2 }\n);\ntry stdout.print(\"{s}\\n\", .{str3});\n```\n:::\n\n\n\n\n\nAs you can imagine, the `replace()` function is used to replace substrings in a string by another substring.\nThis function works very similarly to the `replace()` method from Python strings. Therefore, you\nprovide a substring to search, and every time that the `replace()` function finds\nthis substring within the input string, it replaces this substring with the \"replacement substring\"\nthat you provided as input.\n\nIn the example below, we are taking the input string \"Hello\", and replacing all occurrences\nof the substring \"el\" inside this input string with \"34\", and saving the results inside the\n`buffer` object. As result, the `replace()` function returns an `usize` value that\nindicates how many replacements were performed.\n\n\n\n\n\n::: {.cell}\n\n```{.zig .cell-code}\nconst str1 = \"Hello\";\nvar buffer: [5]u8 = undefined;\nconst nrep = std.mem.replace(\n u8, str1, \"el\", \"34\", buffer[0..]\n);\ntry stdout.print(\"New string: {s}\\n\", .{buffer});\ntry stdout.print(\"N of replacements: {d}\\n\", .{nrep});\n```\n:::\n\n\n\n\n```\nNew string: H34lo\nN of replacements: 1\n```\n\n\n\n\n\n\n## Safety in Zig\n\nA general trend in modern low-level programming languages is safety. As our modern world\nbecomes more interconnected with technology 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, especially 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 operating system where your program is running (e.g. maybe the\nsystem where your code is running has 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 have Zig and other languages 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.\nThere are some memory safety features that you get for free in Zig,\nespecially in arrays and pointer objects. But there are other tools\noffered by the language, that are not used by default.\nIn other words, the `zig` compiler does not obligate you to use such tools.\n\nThe tools listed below are related to memory safety. That is, they help you to achieve\nmemory safety in your Zig code:\n\n- `defer` allows you to keep free operations physically 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 guarantee that your program frees the allocated memory, even if a runtime error occurs.\n- pointers and objects 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 transform your unit tests into 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 has 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 in your program.\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 especially 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. 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 at @sec-vectors-simd;\n- Build System at @sec-build-system;\n\n\n\n\n",
+ "supporting": [
+ "01-zig-weird_files"
+ ],
"filters": [
"rmarkdown/pagebreak.lua"
],
diff --git a/docs/Chapters/01-base64.html b/docs/Chapters/01-base64.html
index 36bf226d..9e6d25c9 100644
--- a/docs/Chapters/01-base64.html
+++ b/docs/Chapters/01-base64.html
@@ -894,8 +894,8 @@
);const allocator = fba.allocator();
-const text = "Testing some more shit";
-const etext = "VGVzdGluZyBzb21lIG1vcmUgc2hpdA==";
+const text = "Testing some more stuff";
+const etext = "VGVzdGluZyBzb21lIG1vcmUgc3R1ZmY=";const base64 = Base64.init();const encoded_text = try base64.encode( allocator, text
@@ -910,8 +910,8 @@
"Decoded text: {s}\n", .{decoded_text});
-
Encoded text: VGVzdGluZyBzb21lIG1vcmUgc2hpdA==
-Decoded text: Testing some more shit
+
Encoded text: VGVzdGluZyBzb21lIG1vcmUgc3R1ZmY=
+Decoded text: Testing some more stuff
You can also see the full source code at once, by visiting the official repository of this book3. More precisely inside the ZigExamples folder4.
“Focus on debugging your application rather than debugging your programming language knowledge”.
This 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 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.
-
The 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e. they obscures the actual source code that you wrote.
+
The 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 a frequent source of confusion. They really make it sometimes hard 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 the code are being sent to the compiler, i.e. they obscure the actual source code that you wrote.
You don’t have macros in Zig. In Zig, the code you write, is the actual code that get’s compiled by the compiler. You also don’t have a 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.
By 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.
This book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):
-
Calin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m), Alexander (@alexwheezy)
+
Calin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m), Alexander (@alexwheezy), Maarten Coopens (@maarteNNNN), Niklas Johansson (@Raphexion)
diff --git a/docs/search.json b/docs/search.json
index c0ff4a2f..80f3c7a2 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -84,7 +84,7 @@
"href": "index.html#acknowledgments",
"title": "Introduction to Zig",
"section": "Acknowledgments",
- "text": "Acknowledgments\nThis book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):\nCalin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m), Alexander (@alexwheezy)",
+ "text": "Acknowledgments\nThis book is also a product of many conversations and exchanges that we had with different people from the Zig community. I (Pedro Duarte Faria) am incredibly grateful for these conversations, and also, for some direct contributions that we had. Below we have a list of the people involved (name of the person with their usename in GitHub):\nCalin Martinconi (@martinconic), Steffen Roller (@sroller), Chris Boesch (@chrboesch), Lv Sihan (@Pokryton), saurabh sharma. (@esskayesss), slackline (@slackline), Markus Kurz (@kurz-m), Rubin Simons (@rubin55), Chris Boesch (@chrboesch), Bruno (@PoorlyDefinedBehaviour), Ilia Choly (@icholy), Korri Katti (@KorryKatti), Vedang Manerikar (@vedang), Tommaso Ricci (@Zorgatone), Primo Sabatini (@primos63), Santiago Fernandez (@santif), Hamza Wahed (@HamzaWahed), mwilbur (@mwilbur), Dima Budaragin (@dbud), Jorge Jímenez (@jorge-j1m), Alexander (@alexwheezy), Maarten Coopens (@maarteNNNN), Niklas Johansson (@Raphexion)",
"crumbs": [
"Welcome"
]
From 5fe95a7450fc424adfe786fea80de53de8de160b Mon Sep 17 00:00:00 2001
From: pedropark99
Date: Sun, 16 Feb 2025 17:14:19 -0300
Subject: [PATCH 069/151] Add leanpub link
---
Assets/leanpub-button.html | 21 +++++++++++++++
Cover/cover-art.svg | 25 +++++++++---------
Cover/cover-artv4.png | Bin 0 -> 577665 bytes
README.md | 7 ++---
_freeze/index/execute-results/html.json | 8 +++---
_quarto.yml | 3 ++-
docs/Cover/cover-artv3.png | Bin 1030663 -> 0 bytes
docs/Cover/cover-artv4.png | Bin 0 -> 577665 bytes
docs/index.html | 33 +++++++++++++++++-------
docs/search.json | 6 ++---
index.qmd | 17 +++++++-----
11 files changed, 81 insertions(+), 39 deletions(-)
create mode 100644 Assets/leanpub-button.html
create mode 100644 Cover/cover-artv4.png
delete mode 100644 docs/Cover/cover-artv3.png
create mode 100644 docs/Cover/cover-artv4.png
diff --git a/Assets/leanpub-button.html b/Assets/leanpub-button.html
new file mode 100644
index 00000000..c063da7f
--- /dev/null
+++ b/Assets/leanpub-button.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/Cover/cover-art.svg b/Cover/cover-art.svg
index a91b5b5b..100bfc3e 100644
--- a/Cover/cover-art.svg
+++ b/Cover/cover-art.svg
@@ -27,9 +27,9 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
- inkscape:zoom="0.5"
- inkscape:cx="491"
- inkscape:cy="366"
+ inkscape:zoom="0.70710678"
+ inkscape:cx="388.20162"
+ inkscape:cy="524.67323"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
@@ -268,13 +268,13 @@
sodipodi:insensitive="true">Introduction to Introduction to ZigZiga project-based booka project-based bookPEDRO DUARTE FARIAPEDRO DUARTE FARIA1st. edition2nd. editioni&{z+@iYsF~ZEWmc%?~N~*|I+TmxB@v8*~lmQ|cuA%2xxxaBT
zy759$j0`DWqJ4dx@tN)?ZCk&hP!AXV`?e!#)i}Xc_@zwK%e|{rz{ePx7<^1`*
zQhIU9AL6M0!Se_et<1%=)zQ}G{+oItDzL!EA3x!KGvWWf1cBP!XsZA3_18E5d-)iP
z|Ha|I8S%e3{4WmwD~A8(!2ja#zc~D_GyK0C_+K&nuNeMU4F5kU29?BfV3qCZA9&W`
zlki4ZYTi(2Cy7k>k!}@p>)9W)N-E69UHF0fhroNg4%4534~y|u6*1}h9HQ9&tJ>_2
z>%)e4wP;84-A#cZL`666QgQzK4g1VzAdqo(%m~z+(FrP@F3g+oXOt+&>(Cz3w?UgL
zYfcDACg3L$Ulh1Ue#LJcSp>{%I4Uvq9b;ot^2Oz4R7!{iClLt5NldX=&u=UAw>w1grbdv_Cm_&;D`k-X1{ERh725WmAt-H-jT1nbUtEkIt9#+pvr
zLUx=NTyK&B$a#BaaBtY)~DOn!h6MOu?3guWm&5r$5sM}850_-+g5VmU_(#`8Z4E+-@kwrmN+=2p{
zrOS9G!+C02*Bus-CobW6N2f>>^6&q|AE3
z^1)Qz(^6-0Y9s3oVrvNGSXPC>mUmo`sL<|E$@FHUMx8ll0=QiGdGMkcZu|Ds%!bie
zN1y%em@+IdE3aEQI`!p2NzKg%kD~t^rsq4PT{6^YYD)^y+~?YJ%CJIdDKQVvYdMAc
z){Z~~pPP!=7F0+nM|(HCmGJc0D)6%oT6-(TPfI(G`57XV`vFYh-0-K2oM%(2qGcA3
z7ke;V>7eHssGAGe8(xEUSICNoar6FS!%ylB_sxY`us|#6vSPSH1I5C%IMsR5$?z6e
zg;6%)xwGI=o~^~L}f7ZMwfpyvnbEnl53j#U!d#GY>r3_T_Yh(kKL?-}SH_Gjf9L`Nzo+HIR
zznX1!=Hsz_6^*8j>~ZF`i40Lgnh7(5_DKZxt}m`UoAx~59ZYr5AY~gMD{JTu>{mwP
z>TTVmuo7KcpK+=%cPW@}wNXcVVI}OQJGDqz_D6|zQg;RiYjY)I2&Ti=2k@<%p5o!-
zA6#YhZlzs%($UVJbSm}|WijbRUN3%qzbQ2qw}Kz!z@&+JLsg)+!pnOPTifbVXyCE^
zJ(#~jl~*H-ce+>CJt^wzG>t80TS3EeMKA3d`r
z#~%j?nSOOf3Z3~z=6j=iUU=5KvM{>j=_9Vn(9rK}`=MslD+GFit4)X&IoOXJ$1$d>!^%Y75+%o^z0Z%}z1vwP;Vn_PM@MNeIE>1AWtK{He9d!%PYxjkH@
z%-&-G6|v;L5~;jERk&&lYX6STrvIaUr<4uRnO@T85t)1qtD_DDQoD)@)z5xc3+%zy
z?|C1iIK@BqBtJJ`v$h)v_I60MJDUtIcD?9(2Lp?^8#>8a7x#LAI28wr)7_|lky-LY
zS9jT>F}r?|5TU*Inf_pJKU0<&WA_7^Cc}=^ooDvInf74^H;WnvL|W4k_l!h0;T-Ry
z^)0e&8oH{$h4Xucs$}S9D{ruWBQ(Xgjb7xqPY|}J-VpNA&)fB(zx8s7Quwhf-{tX2
zc&=Ti2M_*iDf~mF09Gh4?mvqe}j3`_u<1X*z)g>zUo`-(`k-~Q!
zb1nyV+Av7$O!iPMhr^qmH!;^PLv?0)o+;au&3fSDUup9XfA{3H*-ob{l9*>{J0swG)6hca
zXw9d8E6H|G|I>VWSa8BRi6<|h#dW$qj@ow-H6NA;W*>+!8ZLJwAQ0eEYSxg)zP#+c
zVXdk~4Z0vfo3sska2=hgq%sn{`LfdNHqr0MQoHQPct>+@GwrKx)zUNnew>2CEhytmXG{T-dz4RHvQS+Jo)KnsuFyq!KZ*?NIQJK@SG%x_KWg
zO>Qq6YsRXqC8pt^a%yq_45{_%*nJ%CuItxjL!e(clGLkOBItGAE4Xh7d%LL80Z>Ge
zCM?*Bj8#PxrSPaaQ-es4Os9~h!X1IrdWDdI8TO)CkJyvX_L9gEV5tN<#pIz#nFm$v
z=IhkLLsXCuf5tPs7H>MllBF8dwd@VveqXw0Ct3o(ayxAA=rQ4P_Z$HiKu&)(1@ZbxIks3Jc@{
zk6}a>v=*erz%YHW!;P<8-uib*HcHRbpljowgjn
z?E#uV$*f0HY=lE|#i=VIT6Ltb&j-|=^@7chvD#49xA_dV(gkj{wNE{*J}mADEAp-2
zS$5p8=HgTt^dJO<{>w1*x+^3?EYGkNU+dCSqoOhI7Xu8#t!)RO5VKLA$yq!zp=@_c
zh$AgXq@@RL^g&K=cxlUs6h66xCd}J%I6AMkG_)`1Uom62;W})*#!Re2yFuAo-a_Zq
zP{Ysgk~&5Jk|TlU6Fd-Zy>stdN_3TRe6$(~Wg}}ag3nXj
zad5l%0I?3@)Md4r$78!%Q!KIXS&ekt`OFcHEaPW6d{t
zY-yI(L)hjAB0a|u&(e~sG?3rulds1UdSwvjlwj_;8;Ldza!X#*hjmO)S}$AMiqg5T
zlqlxkAGxx93~t-W%+(-2Mu+{k%SqJiB(M|b0oV^TJ&HP9W8ZVza3FJ2&e8Gl5kH;t
zzW5T!-EJ2a`9wZwOkmWk5Pt8U+gudmiUJhLBlCRrn~yM@OdL%+ca7{VahD9`2l{
zW^$0L&E=qYqtMEwSN#7X=k(B7!;!^nMHL<;JSTGX#BXV5PN1(wyB4P
zb1@#1Y7S7ONzaF}=;)4R=mPJ>NHj0_Hg#6Xs&HIo%3gG!6S3bnP(?*|Z+|CQ;gem$
zY-!8!q3dHqDYNC#a5p8}hyMmJ%;QGfqIE05TF|1E5wstbK{*As>6oJVNN+uxD%;!a
z6YEC?qT1V+L>^ATZTHhDZM`n}$MpI)najPWVpzIEOmA6ZC#YPU7gi%LFA}nf8hdho
zP8XIwi182y5rbl$Wju>{6ET`u*LLCgqVEmMHMySmC$8j1
zJ)zX5pz@-;T4BO>yW^$1OWIapb!9%L3^PiR)BmDUV3`HHCc@&kdzHwf|+~5@Nq=DMy9z_Ugr#9sim3jGlSDh%Nmfpl2NMES_FT6IphiKr+ZKlxC
za4EW$Szg8cD)iD0S)-Ufz*DbXkwqs`w-cDWe_plxOcngWNSzmVFp>?rNp^@1x($2O
z1l{GCKzPb+Z|zKmN}qeVEq%P58JU?h<^Yj$Y);xn0W?O&V;^iI#04~KohARmbwJ)2
zN;=lIp~caWQ0q3aK9>$k-mkOC{%~)(VDMp%g%&oD5?wN<`yccKf%LI}1`t18<;*AY
zP;w@?CIL5kXcUUIL`TtQZTbrz&L-JBB2C5Dqs+6-cAduV(vxcw+2ewtX_0h4Tu>@Z
z2!kuuvXcWTdCx|o({0lMC;>{C;ZZ7b74P3k{>5lLSwe&3;-0BFL-uT!3Bk?N=EOkA
zyBEJUm&1pJMQpOQL#1a@si3`6`i1>Bdpm8Bl2;irWq^_mU
z?V*h(s!OP#S>X&odq*0`Rf7r*^z%u}ZdRiz%m?gLr}Zjw!OJdCHbv%}SIUNVcqld1t>%tr+!xX>zwI~rgg{pWdN@)i8CEp7EXO!nusL8{-H2=R}!
zyj@tScZ>V4K_8PFi_t)S6`KGo!%8>=-{NR-GCdCtx9>bifuD@ri%eV1o?_B~;?-Vi
zwkIj+B|>J>csX=$2l=_GiuifXu%O7&Z$#y(Ph56>Lui%ygDw*w|hiAUpy^0
zbg2N5_*VDye&x%^^sX2MsLAng=NPWQZx0ars+o8q@!*Z2;U|YydbKb7SYwO>`oEpqsCYEv$4&5`QSn+$Xb6hY7`)|Ym
zP+}K~WxU$#T@ur2#4B?iV$X6>LE-~bG$1_ucTLGK!E_;n;!kMlyk>RSM;@R?jXHOv
zn_z__X(#!;hUz6laHMO{jrs)?bH&BmYpVzM=v0*&s>I)V5fB3V5#h^L39>khakoYU
zjHKTF=NoAwT;kOmAz4MGwsnvzwUIGIQ7fv&dqoq<`FbW@;q%Snp3o5+O)?f2Cu3o8
zF|o)vOa!l`d_%OxTpNO&Dh0GO;oSs^@JQ
zkXXb2b`ZA05pW4D+$h6tMkf#m0@S>|sOR$UD%jEe$@X`v**hBfl-hbDir(PSCzZ-T
zVX!Q_8>c=*@_Tyi-=8#5j>bv--?IQ6CupAVDC$NghPtVqN}$lzjRcH5%!~$;rV0P!
z6qo=`0cA9r+Bvk$W}cLHahae~(B&Z)|6@(_#qAZMRXETN|7K+5dv{w2G%
zH8s-gu|yWsg$0BA;ivRBB(958J@3stqcoXlMXE0%Ntqx+mg?8i*ofbuUTh4>hau%2Hs~JQ0dOp;Ws8KAfcZVbhhE9m
z9yRV>!~W>Zmri|;ufU5WVfx1jd7=xm@QJI!Pa1OmwxZI;SS
zcjYb`UA5z}dL{x?x95?1UD=|wj6KB*W5nr;c1pSLKp=vM
z(IG0
zW}%NaGwNHTL^KRUN0k3r;
zPB#g)GPBBW*V3xDXTuiEJ?z_^>V55#9``&yO`9DS#i5Hw(zZF;g0n_TdyN)R(c+tp
z5sTu>bx_Au=cAST&I%x0M0;{WPR~L5h5(H$*JB}cut$6e=}ytDJW97BC1vieJrHE(
zBl_5=q-c&Z^&T*xMQ$>T0LQSUNzF?Ahm7CucXReGaI>eP(COr%x31BWI2VOVr$H1s
zOjX$yV6K;w!30KkU$y=dofsfGchi%7*a!}4?U#(~R7)*+rPwHyD$JN{e>LU!JS@Df
z$LYuBxEgPoTf)#3+#hY75#P&MJ}2Fq{Av8oPa7L_u<^rm2^#f&r)a<=fTajWaQ{I8
zCDjKKgZ2}ng&m~4t^=eE#m(UPGr*Rh$u^?cMB#Hzz=
z1og(JWYkS9odx>a8rcH~u<(P8>r3$wmeYyAaZEJ9(Z|?-q#$
z%hIbC+^f)U+uX&=PX)YpX#A0gKx1z7eOHlA825wK+USA*{R3HUqQA|HF%lwfZ%#k$8GT*UVpsLRmeQTZUO;G*A*5?LHyCr~5AXC~tRtfpQ
zsZxBI43`y2jKCZbe!YA;|2eB;VHs-5T<3
zBBikQLSqKtVW;ULjJoHysMI9Szlgn`++e9yXD6w6bXN}Z3o5op`?xGUlx1wnAdRBz
zwMdAV?aJ5UB|cJLx4*oAykYb4T-daCHa=Ugy?(^hEilAU(@4%$y1H)qi?kyR&>0|$
zU|~*;NJ_e#*=nG>TEB`uPYHqjQ}wn7{6&rXRhJpeC?#bhnnw#8Fr||wYfshJa)U)9
z2?xf&d@sH4pc+*MuRUsW?vAwc7r{^D^M#^Un%LGI?T`Jf?awk|;j?rChQPE#5vbT}
zEgmS?%iSF-x{6nrjQqsD*Z;tNAD_~`zv2&N2kvj($Ewe7O``^$G3hyg|qiduGO5_`Env;T|jBw`Kp1aU8;gn$8`p)U8)k
zzhKoGK8nv1(h^TBpGCamrx<@2r0tj_jVY8+6DNIXB$|P(N~2vSnk4ruN68PmD;6^6
zU&;JVF2QwmwSst(_iD?TBj!z4u;En6ToM;e^c+Q>J)zrvxDwNAdfl7755;=nREoiP
zfje^Eht3&N-```@Yv&!_sik0^5AjU$Xeg+9?h2?Wj7R^%AW+rcqw6426C!O>JT%d7
zVj^0p#)=$>U$@oP!A#&w(
zGUg$21|hokyJ-uXlkM!xW80INV;P_0g|VO)UkT?T`3$0hb&PL+$)VE(I*Eo62jX>T
zNHr6ybA0WgcT{d#dMh-v)UGRkCP0)M8QDE)Uy&3?9u+6XMo2}q#9GE>)EO;Me3d~F
z5`AMf`jG}*LxA_0D;xDwqo)greU6M!I)2@VaI$w3?zfVfnK
z_dQ47uuBCLkz~Vv>_~nMbM+;tt{n@P_j%h>L`~dbjA-+-j-z=>;F!f_Zv37B5-G0v
z3mBMv!GFtY+0%M!YPGBdk!{)=TNDo?(Fx3sfb6#NAeX)HV*>m=-xCTMA1
zmsP(Qm>CY7a4p=}9Sw)V`jg3lZ5gUrT6w(YYkC5v_$R?8uQ+Y$@!`Q=3}m7aGfy*z
zpPyq&e2af^;A*4F<9lA_6H2Y^J@Zg5r?ozBMu;^UdxSq^cQv@$D
zEqxbJ6_Z!0853QzV1c^pI!(eQj$N9R9?Zi2^>f_6mWgbCsu)`_HCU){
z#>~?-sYH%KX!^&JyX>XAI_1R%FY@=u*zd}Bbmd-(-L~_?P7>kB-0faG8A|d9bGRXw
ziv}SqOSma7f>uFcJov$;1j-1G%gW}q`!#jvubIvxAm++v1%j|z8o8gmIck@wury3JSmv*uIRk4Z{5K;@yYRt1w|Y9y%>;@U%zbgo@#lV0X5D{_YLBFlse=*TyjHZ
z2Nf1<7-k~U$$#lMEY+U*t&bE;+kY^0_n17wMY!%lD?1KDk4e0)IHn-2VX``kAa1_Y
zMVUUzkF}D~!=^TFFVw064#|HZm68FibwN8|$vrCHw(_5a5y5O(e#PJV%z(bxmKTaF
zcEj}=tjT|V><}0(T`wl6B-;&7fjvLyX~#XU)BYV6VV3t=?BLF?{&B=yOG9)E_xcZf0V@J@jQEY$MvSZ7V1#!^LTQz)#PnTi;
z_V;Z1>$KVN_(B0p7b>f+Rv>9oRsx^Bi>Req?lzdJw6k)YU_plF!V_|!T8h;byTt&
zsX;YxdiUZZMvf|jS4}GhU!S!{$Yf9bxsGVL*_naBY9JA$
zC6RypU#-=5r3Kd86pl+4Gz>-8XjvYoQ?rzxDj1ETv1DP<)Z2NyBp#CM9!0^o
z@m(SC75q^r!gk>%QX}Pmn#Zsjezns}X&A%$LYmM&-F0d$$QARJM{y}#hKeXEo~4rO
zb-dnjQNdKSEsx6DYK7?}?}EOWg6vCBc)x6$^kDo#FU?Pl_bksG^B7)pr{0Aj8^u{#
z7gVAiZZ$r&;eL9ft{&x^TjIZ1^F-I0G$e7ad!QMEr-P73o6wz9oeY=u+rhUEho4iS
zrxB(4)!aMA7G&kUF_5Zr`t3Y(7`Iss9mS2z3qF0hc6>|LXsxU<*nU&IOTbPirX8Eh0$B+3#ZYuX)p=pjGLiEUy@#a&U-9j!w)>_~&JVDx55FGLhTF_B
zPcYU?mPb=Azhjp+a#BW^=GnaNhK91=an5
zND+>IZEYM95_mPbiUm(o-?8IFn9>_vU)JjusXJ}Vhq!pgp;O`Kc7MQ}u%BzLwprkl
z`b6kI;#=5Ws18w>s$e(H4O)vXsMhX&VUn
zeffEXt~}dJW)#BoY$lF9&dp?e6dfm(Tro5!xRbP`D8`c?^Cb+<*|CH-va
z(dc$cpj{#kER~OES*|orz4;UQ