diff --git a/Chapters/01-zig-weird.qmd b/Chapters/01-zig-weird.qmd index 4c9d4f68..2d0f3105 100644 --- a/Chapters/01-zig-weird.qmd +++ b/Chapters/01-zig-weird.qmd @@ -47,7 +47,7 @@ This philosophy becomes clear with the following phrase from the official websit > "Focus on debugging your application rather than debugging your programming language knowledge". -This phrase is specially true for C++ programmers. Because C++ is a gigantic language, +This phrase is especially true for C++ programmers. Because C++ is a gigantic language, with tons of features, and also, there are lots of different "flavors of C++". These elements are what makes C++ so complex and hard to learn. Zig tries to go in the opposite direction. Zig is a very simple language, more closely related to other simple languages such as C and Go. @@ -75,9 +75,9 @@ behaviour in edge situations. Once again, less is more. ## Hello world in Zig We begin our journey in Zig by creating a small "Hello World" program. -To start a new Zig project in your computer, you simply call the `init` command +To start a new Zig project on your computer, you simply call the `init` command from the `zig` compiler. -Just create a new directory in your computer, then, init a new Zig project +Just create a new directory on your computer, then, init a new Zig project inside this directory, like this: ```bash @@ -141,7 +141,7 @@ of compiling and building complex projects, easier. Examples of build systems are CMake, GNU Make, GNU Autoconf and Ninja, which are used to build complex C and C++ projects. With these systems, you can write scripts, which are called "build scripts". -They simply are scripts that describes the necessary steps to compile/build +They simply are scripts that describe the necessary steps to compile/build your project. However, these are separate tools, that do not @@ -169,7 +169,7 @@ and install the library in your system, and just link your source code with the library at the build step of your project. However, if this external Zig library is available on GitHub for example, -and it has a valid `build.zig.zon` file in root folder of the project, +and it has a valid `build.zig.zon` file in the root folder of the project, which describes the project, you can easily include this library in your project by simply listing this external library in your `build.zig.zon` file. @@ -314,7 +314,7 @@ module is by default private to this Zig module and can only be called from with Unless, you explicitly mark this function as a public function with the `pub` keyword. If you think about it, this `pub` keyword in Zig does essentially the opposite of what the `static` keyword -do in C/C++. By making a function "public" you allow other Zig modules to access and call this function. +does in C/C++. By making a function "public" you allow other Zig modules to access and call this function. A calling Zig module imports another module by using the `@import()` built-in function, which makes all public functions from the imported module visible to the calling Zig module. @@ -707,7 +707,7 @@ to calculate the value of another object, or, you can call a method that belongs particular object. It doesn't matter in which way you use it. As long as you use it. -If you try to break this rule, i.e., if your try to declare a object, but not use it, +If you try to break this rule, i.e., if your try to declare an object, but not use it, the `zig` compiler will not compile your Zig source code, and it will issue a error message warning that you have unused objects in your code. @@ -734,7 +734,7 @@ Everytime you declare a new object in Zig, you have two choices: To explicitly discard the value of any object (constant or variable), all you need to do is to assign this object to a special character in Zig, which is the underscore (`_`). -When you assign an object to a underscore, like in the example below, the `zig` compiler will automatically +When you assign an object to an underscore, like in the example below, the `zig` compiler will automatically discard the value of this particular object. You can see in the example below that, this time, the compiler did not @@ -806,7 +806,7 @@ t.zig:7:5: note: consider using 'const' ## Primitive Data Types {#sec-primitive-data-types} -Zig have many different primitive data types available for you to use. +Zig has many different primitive data types available for you to use. You can see the full list of available data types at the official [Language Reference page](https://ziglang.org/documentation/master/#Primitive-Types)[^lang-data-types]. @@ -960,7 +960,7 @@ Because with this length number the `zig` compiler can easily check if you are trying to access an index that is out of the bounds of this particular slice, or, if you are causing any buffer overflow problems. In the example below, we access the `len` property of the slice `sl`, which tells us that this slice -have 2 elements in it. +has 2 elements in it. ```{zig} #| auto_main: true @@ -1139,7 +1139,7 @@ if (x == 124 and y == 124) { -## How strings work in Zig? {#sec-zig-strings} +## How do strings work in Zig? {#sec-zig-strings} The first project that we are going to build and discuss in this book is a base64 encoder/decoder (@sec-base64). But in order for us to build such a thing, we need to get a better understanding on how strings work in Zig. @@ -1152,7 +1152,7 @@ In summary, there are two types of string values that you care about in Zig, whi A string literal value is just a pointer to a null-terminated array of bytes (i.e., similar to a C string). But in Zig, a string literal value also embeds the length of the string into the data type of the value itself. -Therefore, a string literal value have a data type in the format `*const [n:0]u8`. The `n` in the data type +Therefore, a string literal value has a data type in the format `*const [n:0]u8`. The `n` in the data type indicates the size of the string. On the other hand, a string object in Zig is basically a slice to an arbitrary sequence of bytes, @@ -1163,7 +1163,7 @@ marked as constant with `const`, or as variable with `var`. Because a string object is essentially a slice, it means that a string object always contains two things: a pointer to an array of bytes (i.e., `u8` values) that represents the string value; and also, a length value, which specifies the size of the slice, or, how many elements there is in the slice. -It's worth to emphasize that the array of bytes in a string object is not null-terminated, like in a +It's worth emphasizing that the array of bytes in a string object is not null-terminated, like in a string literal value. ```{zig} @@ -1175,7 +1175,7 @@ const object: []const u8 = "A string object"; ``` Zig always assumes that the sequence of bytes in your string is UTF-8 encoded. This might not be true for every -sequence of bytes you're working with, but is not really Zig's job to fix the encoding of your strings +sequence of bytes you're working with, but it's not really Zig's job to fix the encoding of your strings (you can use [`iconv`](https://www.gnu.org/software/libiconv/)[^libiconv] for that). Today, most of the text in our modern world, especially on the web, should be UTF-8 encoded. So if your string literal is not UTF-8 encoded, then, you will likely have problems in Zig. @@ -1235,13 +1235,13 @@ the string inside the object itself. In the case of a string literal value, this data type of the value (i.e., the `n` variable in `[n:0]u8`). While, in a string object, the length is stored in the `len` attribute of the slice that represents the string object. This small detail makes your code safer, because it's much easier for the Zig compiler to check if you are trying to access an element that is -"out of bounds", i.e., if your trying to access memory that does not belong to you. +"out of bounds", i.e., if you're trying to access memory that does not belong to you. To achieve this same kind of safety in C, you have to do a lot of work that kind of seems pointless. So getting this kind of safety is not automatic and much harder to do in C. For example, if you want to track the length of your string throughout your program in C, then, you first need to loop through the array of bytes that represents this string, and find the null element (`'\0'`) position to discover -where exactly the array ends, or, in other words, to find how much elements the array of bytes contain. +where exactly the array ends, or, in other words, to find how many elements the array of bytes contains. To do that, you would need something like this in C. In this example, the C string stored in the object `array` is 25 bytes long: @@ -1374,23 +1374,23 @@ to represent the number 570. That is why the relationship between bytes and unic 1 to 1. Each unicode point is a single character in the string, but not always a single byte corresponds to a single unicode point. -All of this means that if you loop trough the elements of a string in Zig, you will be looping through the +All of this means that if you loop through the elements of a string in Zig, you will be looping through the bytes that represents that string, and not through the characters of that string. In the Ⱥ example above, the for loop needed two iterations (instead of a single iteration) to print the two bytes that represents this Ⱥ letter. -Now, all english letters (or ASCII letters if you prefer) can be represented by a single byte in UTF-8. As a -consequence, if your UTF-8 string contains only english letters (or ASCII letters), then, you are lucky. Because +Now, all English letters (or ASCII letters if you prefer) can be represented by a single byte in UTF-8. As a +consequence, if your UTF-8 string contains only English letters (or ASCII letters), then, you are lucky. Because the number of bytes will be equal to the number of characters in that string. In other words, in this specific situation, the relationship between bytes and unicode points is 1 to 1. But on the other side, if your string contains other types of letters… for example, you might be working with -text data that contains, chinese, japanese or latin letters, then, the number of bytes necessary to represent +text data that contains, Chinese, Japanese or Latin letters, then, the number of bytes necessary to represent your UTF-8 string will likely be much higher than the number of characters in that string. If you need to iterate through the characters of a string, instead of its bytes, then, you can use the `std.unicode.Utf8View` struct to create an iterator that iterates through the unicode points of your string. -In the example below, we loop through the japanese characters “アメリカ”. Each of the four characters in +In the example below, we loop through the Japanese characters “アメリカ”. Each of the four characters in this string is represented by three bytes. But the for loop iterates four times, one iteration for each character/unicode point in this string: @@ -1474,9 +1474,9 @@ try stdout.print( ); ``` -The `concat()` function, as the name suggests, concatenate two or more strings together. +The `concat()` function, as the name suggests, concatenates two or more strings together. Because the process of concatenating the strings involves allocating enough space to -accomodate all the strings together, this `concat()` function receives an allocator +accommodate all the strings together, this `concat()` function receives an allocator object as input. ```{zig} @@ -1588,7 +1588,7 @@ also has some rules that help you to achieve another type of safety, which is mo program logic safety. These rules are: - pointers and objects are non-nullable by default. Which eliminates an edge case that might break the logic of your program. -- switch statements must exaust all possible options. +- switch statements must exhaust all possible options. - the `zig` compiler forces you to handle every possible error in your program.