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 01/10] 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 02/10] 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 03/10] 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 04/10] 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 @@

2 => continue :xsw 3, 3 => return, 4 => {}, -} + else => { + try stdout.print( + "Unmatched case, value: {d}\n", .{@as(u8, 1)} + ); + }, +} diff --git a/docs/index.html b/docs/index.html index cdf70e0c..6725b3d8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,7 +7,7 @@ - + Introduction to Zig \n\n\n\n \n\n\n\n\n### Sending donations directly\n\nYou can also donate some amount directly to the author of the project via:\n\n- PayPal Donation.\n- Revolut.\n\nThese are good ways to support directly the author of the project, which helps to foster\nmore contents like this, and it makes possible for the author to keep writing helpful tools and\nmaterials for the community.\n\n### PayPal\n\n\n\n \n \n \n \n \n \n\n\n\n### Revolut\n\nYou can send money via Swift Payment with the following bank and Swift details:\n\n```\nRecipient: Pedro Duarte Faria\nBIC/SWIFT Code: REVOSGS2\nAccount number: 6124512226\nName and address of the bank: Revolut Technologies Singapore Pte. Ltd, 6 Battery Road, Floor 6-01, 049909, Singapore, Singapore\nCorresponding BIC: CHASGB2L\n```\n\nIf you do have a Revolut account, you can scan the following QR code:\n\n\n\n \n \n \n \n \n\n\n\n:::\n\n\n## About this book {.unnumbered}\n\nThis an open (i.e. it is open-source), technical and introductory book for the [Zig programming language](https://ziglang.org/),\nwhich is a new general purpose, and low-level programming language for building optimal and robust software.\n\nOfficial repository of the book: .\n\nThis book is designed for both beginners and experienced developers. It explores the exciting world of Zig through small\nand simple projects (in a similar style to the famous \"Python Crash Course\" book from Eric Matthes).\nSome of these projects are: a Base64 encoder/decoder, a HTTP Server and an image filter.\n\nAs you work through the book, you will learn:\n\n- The syntax of the language, and how it compares to C, C++ and Rust.\n- Data structures, memory allocators, filesystem and I/O.\n- Optionals as a new paradigm to handle nullability.\n- How to test and debug a Zig application.\n- Errors as values, and how to handle them.\n- How to build C and Zig code with the build system that is embedded into the language.\n- Zig interoperability with C.\n- Parallelism with threads and SIMD.\n- And more.\n\n\n## About the author {.unnumbered}\n\nPedro Duarte Faria have a bachelor degree in Economics from Federal University of Ouro Preto - Brazil.\nCurrently, he is a Data Platform Engineer at [Blip](https://www.blip.ai/en/)[^blip], and\nan Associate Developer for Apache Spark 3.0 certified by Databricks.\n\n[^blip]: \n\n\nThe author have more than 4 years of experience in the data industry. Developing data products, pipelines,\nreports and analysis for research institutions and some of the largest companies in the\nbrazilian financial sector, such as the BMG Bank, Sodexo and Pan Bank.\n\nBut Pedro is also a passionate software developer that loves to\nlearn and teach about programming.\nAlthough Pedro uses many different languages in his work, he is specialized in the R programming language, and have given several\nlectures and courses about it, inside graduate centers (such as PPEA-UFOP^[]),\nin addition to federal and state organizations (such as FJP-MG^[]).\n\n\nPersonal Website: \n\nLinkedin: \n\nMastodon: [\\@pedropark99\\@fosstodon.org](https://fosstodon.org/@pedropark99)\n\nTwitter (X): [\\@PedroPark9](https://twitter.com/PedroPark9)\n\n## License {.unnumbered}\n\nCopyright © 2024 Pedro Duarte Faria. This book is licensed by the [CC-BY 4.0 Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/)[^cc-license].\n\n[^cc-license]: \n\n![](Figures/creative-commoms-88x31.png){width=88px}\n\n\n## Book compilation metadata {.unnumbered}\n\nThis book was compiled using the following versions of [Zig](https://ziglang.org) and [Quarto](https://quarto.org):\n\n\n\n\n\n- System version: Linux, 6.8.0-48-generic, NA, x86_64.\n- Zig version: 0.14.0-dev.2051+b1361f237.\n- Quarto version: 1.5.57.\n\n\n\n\n\n## Book citation {.unnumbered}\n\nYou can use the following BibTex entry to cite this book:\n\n```\n@book{pedro2024,\n author = {Pedro Duarte Faria},\n title = {Introduction to Zig},\n subtitle = {a project-based book},\n month = {October},\n edition = {1},\n year = {2024},\n address = {Belo Horizonte},\n url = {https://github.com/pedropark99/zig-book}\n}\n```\n\n## Corresponding author and maintainer {.unnumbered}\n\nPedro Duarte Faria\n\nContact: [pedropark99\\@gmail.com](mailto:pedropark99@gmail.com)\n\nPersonal website: \n\n\n## Acknowledgments {.unnumbered}\n\nThis book is also a product of many conversations and exchanges that we had\nwith different people from the Zig community. I (Pedro Duarte Faria) am incredibly\ngrateful for these conversations, and also, for some direct contributions that we\nhad. Below we have a list of the people involved (name of the person with their usename in GitHub):\n\n\n\n\n\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)\n", + "markdown": "---\nengine: knitr\nknitr: true\nsyntax-definition: \"./Assets/zig.xml\"\n---\n\n\n\n\n\n\n\n\n::: {.content-visible when-format=\"html\"}\n\n# Welcome {.unnumbered}\n\nWelcome! This is the initial page for the \"Open Access\" HTML version of the book \"Introduction to Zig: a project-based book\",\nwritten by [Pedro Duarte Faria](https://pedro-faria.netlify.app/).\nThis is an open book that provides an introduction to the [Zig programming language](https://ziglang.org/),\nwhich is a new general-purpose, and low-level language for building robust and optimal software.\n\n## Support the project! {.unnumbered}\n\nIf you like this project, and you want to support it, you can buy an eBook or a physical copy\nof the book on Amazon:\n\n\n\n\n\n\n \n\n\n\n\n### Sending donations directly\n\nYou can also donate some amount directly to the author of the project via:\n\n- PayPal Donation.\n- Revolut.\n\nThese are good ways to support directly the author of the project, which helps to foster\nmore contents like this, and it makes possible for the author to keep writing helpful tools and\nmaterials for the community.\n\n### PayPal\n\n\n\n \n \n \n \n \n \n\n\n\n### Revolut\n\nYou can send money via Swift Payment with the following bank and Swift details:\n\n```\nRecipient: Pedro Duarte Faria\nBIC/SWIFT Code: REVOSGS2\nAccount number: 6124512226\nName and address of the bank: Revolut Technologies Singapore Pte. Ltd, 6 Battery Road, Floor 6-01, 049909, Singapore, Singapore\nCorresponding BIC: CHASGB2L\n```\n\nIf you do have a Revolut account, you can scan the following QR code:\n\n\n\n \n \n \n \n \n\n\n\n:::\n\n\n## About this book {.unnumbered}\n\nThis an open (i.e. it is open-source), technical and introductory book for the [Zig programming language](https://ziglang.org/),\nwhich is a new general purpose, and low-level programming language for building optimal and robust software.\n\nOfficial repository of the book: .\n\nThis book is designed for both beginners and experienced developers. It explores the exciting world of Zig through small\nand simple projects (in a similar style to the famous \"Python Crash Course\" book from Eric Matthes).\nSome of these projects are: a Base64 encoder/decoder, a HTTP Server and an image filter.\n\nAs you work through the book, you will learn:\n\n- The syntax of the language, and how it compares to C, C++ and Rust.\n- Data structures, memory allocators, filesystem and I/O.\n- Optionals as a new paradigm to handle nullability.\n- How to test and debug a Zig application.\n- Errors as values, and how to handle them.\n- How to build C and Zig code with the build system that is embedded into the language.\n- Zig interoperability with C.\n- Parallelism with threads and SIMD.\n- And more.\n\n\n## About the author {.unnumbered}\n\nPedro Duarte Faria have a bachelor degree in Economics from Federal University of Ouro Preto - Brazil.\nCurrently, he is a Data Platform Engineer at [Blip](https://www.blip.ai/en/)[^blip], and\nan Associate Developer for Apache Spark 3.0 certified by Databricks.\n\n[^blip]: \n\n\nThe author have more than 4 years of experience in the data industry. Developing data products, pipelines,\nreports and analysis for research institutions and some of the largest companies in the\nbrazilian financial sector, such as the BMG Bank, Sodexo and Pan Bank.\n\nBut Pedro is also a passionate software developer that loves to\nlearn and teach about programming.\nAlthough Pedro uses many different languages in his work, he is specialized in the R programming language, and have given several\nlectures and courses about it, inside graduate centers (such as PPEA-UFOP^[]),\nin addition to federal and state organizations (such as FJP-MG^[]).\n\n\nPersonal Website: \n\nLinkedin: \n\nMastodon: [\\@pedropark99\\@fosstodon.org](https://fosstodon.org/@pedropark99)\n\nTwitter (X): [\\@PedroPark9](https://twitter.com/PedroPark9)\n\n## License {.unnumbered}\n\nCopyright © 2024 Pedro Duarte Faria. This book is licensed by the [CC-BY 4.0 Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/)[^cc-license].\n\n[^cc-license]: \n\n![](Figures/creative-commoms-88x31.png){width=88px}\n\n\n## Book compilation metadata {.unnumbered}\n\nThis book was compiled using the following versions of [Zig](https://ziglang.org) and [Quarto](https://quarto.org):\n\n\n\n\n- System version: Linux, 6.8.0-49-generic, NA, x86_64.\n- Zig version: 0.14.0-dev.2051+b1361f237.\n- Quarto version: 1.5.57.\n\n\n\n\n## Book citation {.unnumbered}\n\nYou can use the following BibTex entry to cite this book:\n\n```\n@book{pedro2024,\n author = {Pedro Duarte Faria},\n title = {Introduction to Zig},\n subtitle = {a project-based book},\n month = {October},\n edition = {1},\n year = {2024},\n address = {Belo Horizonte},\n url = {https://github.com/pedropark99/zig-book}\n}\n```\n\n## Corresponding author and maintainer {.unnumbered}\n\nPedro Duarte Faria\n\nContact: [pedropark99\\@gmail.com](mailto:pedropark99@gmail.com)\n\nPersonal website: \n\n\n## Acknowledgments {.unnumbered}\n\nThis book is also a product of many conversations and exchanges that we had\nwith different people from the Zig community. I (Pedro Duarte Faria) am incredibly\ngrateful for these conversations, and also, for some direct contributions that we\nhad. Below we have a list of the people involved (name of the person with their usename in GitHub):\n\n\n\n\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)\n", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/contributors.txt b/contributors.txt index 17bbcd5c..18cad9e2 100644 --- a/contributors.txt +++ b/contributors.txt @@ -11,3 +11,4 @@ Chris Boesch,@chrboesch Bruno,@PoorlyDefinedBehaviour Ilia Choly,@icholy Korri Katti,@KorryKatti +Vedang Manerikar,@vedang diff --git a/docs/index.html b/docs/index.html index 6725b3d8..73c18985 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,7 +7,7 @@ - + Introduction to Zig