Skip to content

Commit 3a02942

Browse files
authored
Merge pull request #65 from pedropark99/revision-16
Add revision for chapter 16 and fix issue
2 parents 417c906 + c883060 commit 3a02942

File tree

50 files changed

+669
-598
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+669
-598
lines changed

Chapters/01-base64.qmd

+6-6
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ the base64 encoding system. We want to produce the sequence of base64 characters
237237
original message in the base64 encoding system.
238238

239239
In contrast, "decode" represents the inverse process.
240-
We want to decode, or, in other words, translate a base64 message back to it's original content.
240+
We want to decode, or, in other words, translate a base64 message back to its original content.
241241
So, in this process we get a sequence of base64 characters as input, and produce as output,
242242
the binary data that is represented by this sequence of base64 characters.
243243

@@ -251,13 +251,13 @@ that converts a sequence of base64 characters back into the original sequence of
251251

252252
One task that we need to do is to calculate how much space we need to reserve for the
253253
output, both of the encoder and decoder. This is simple math, and can be done easily in Zig
254-
because every array have it's length (it's number of elements) easily accesible by consulting
254+
because every array have its length (its number of elements) easily accesible by consulting
255255
the `.len` property of the array.
256256

257257
For the encoder, the logic is the following: for each 3 bytes that we find in the input,
258258
4 new bytes are created in the output. So, we take the number of bytes in the input, divide it
259259
by 3, use a ceiling function, then, we multiply the result by 4. That way, we get the total
260-
number of bytes that will be produced by the encoder in it's output.
260+
number of bytes that will be produced by the encoder in its output.
261261

262262
The `_calc_encode_length()` function below encapsulates this logic.
263263
Inside this function, we take the length of the input array,
@@ -469,10 +469,10 @@ bits from the first and second bytes in the input string. But how
469469
can we do that? The answer relies on the *bitwise and* (`&`) operator.
470470

471471
The @fig-encoder-bitshift already showed you what effect this `&` operator
472-
produces in the bits of it's operands. But let's make a clear description of it.
472+
produces in the bits of its operands. But let's make a clear description of it.
473473

474474
In summary, the `&` operator performs a logical conjunction operation
475-
between the bits of it's operands. In more details, the operator `&`
475+
between the bits of its operands. In more details, the operator `&`
476476
compares each bit of the first operand to the corresponding bit of the second operand.
477477
If both bits are 1, the corresponding result bit is set to 1.
478478
Otherwise, the corresponding result bit is set to 0 [@microsoftbitwiseand].
@@ -559,7 +559,7 @@ just for brevity reasons. So, just remember that this function is a public funct
559559
Furthermore, this `encode()` function have two other arguments:
560560

561561
1. `input` is the input sequence of characters that you want to encode in base64;
562-
2. `allocator` is an allocator object to use in the necessary memory allocations.
562+
1. `allocator` is an allocator object to use in the necessary memory allocations.
563563

564564
I described everything you need to know about allocator objects at @sec-allocators.
565565
So, if you are not familiar with them, I highly recommend you to comeback to

Chapters/01-memory.qmd

+15-20
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Knowing the length (or the size) of each object is also important. So the length
5757
in some cases, *known at compile time*.
5858

5959
The `zig` compiler cares more about knowing the length (or the size) of a particular object
60-
, than to know it's actual value. But, if the `zig` compiler knows the value of the object, then, it
60+
, than to know its actual value. But, if the `zig` compiler knows the value of the object, then, it
6161
automatically knows the size of this object. Because it can simply calculate the
6262
size of the object by looking at the size of the value.
6363

@@ -84,7 +84,7 @@ but this array have a known fixed size, like `[60]u8` (which declares an array o
8484
this type, or, this struct that you are declaring, becomes a type with a known fixed size at compile-time.
8585
And because of that, in this case, the `zig` compiler do not need to known at compile-time the exact value of
8686
any object of this type. Since the compiler can find the necessary size to store this object by
87-
looking at the size of it's type.
87+
looking at the size of its type.
8888

8989

9090
Let's look at an example. In the source code below, we have two constant objects (`name` and `array`) declared.
@@ -118,14 +118,14 @@ For example, the function `input_length()` contains an argument named `input`, w
118118
Is impossible to know at compile time the value of this particular argument. And it also is impossible to know the size/length
119119
of this particular argument. Because it is an array that do not have a fixed size specified explicitly in the argument type annotation.
120120

121-
So, we know that this `input` argument will be an array of `u8` integers. But we do not know at compile-time, it's value, and neither his size.
121+
So, we know that this `input` argument will be an array of `u8` integers. But we do not know at compile-time, its value, and neither his size.
122122
This information is known only at runtime, which is the period of time when you program is executed.
123123
As a consequence, the value of the expression `input.len` is also known only at runtime.
124124
This is an intrinsic characteristic of any function. Just remember that the value of function arguments is usually not "compile-time known".
125125

126126
However, as I mentioned earlier, what really matters to the compiler is to know the size of the object
127-
at compile-time, and not necessarily it's value. So, although we don't know the value of the object `n`, which is the result of the expression
128-
`input.len`, at compile-time, we do know it's size. Because the expression `input.len` always return a value of type `usize`,
127+
at compile-time, and not necessarily its value. So, although we don't know the value of the object `n`, which is the result of the expression
128+
`input.len`, at compile-time, we do know its size. Because the expression `input.len` always return a value of type `usize`,
129129
and the type `usize` have a known fixed size.
130130

131131

@@ -228,7 +228,7 @@ Because they will be automatically destroyed once the stack space is freed at th
228228
So, once the function call returns (or ends, if you prefer to call it this way)
229229
the space that was reserved in the stack is destroyed, and all of the objects that were in that space goes away with it.
230230
This mechanism exists because this space, and the objects within it, are not necessary anymore,
231-
since the function "finished it's business".
231+
since the function "finished its business".
232232
Using the `add()` function that we exposed above as an example, it means that the object `result` is automatically
233233
destroyed once the function returns.
234234

@@ -238,7 +238,7 @@ freed/destroyed at the end of the function scope.
238238
:::
239239

240240

241-
This same logic applies to any other special structure in Zig that have it's own scope by surrounding
241+
This same logic applies to any other special structure in Zig that have its own scope by surrounding
242242
it with curly braces (`{}`).
243243
For loops, while loops, if else statements, etc. For example, if you declare any local
244244
object in the scope of a for loop, this local object is accessible only within the scope
@@ -319,14 +319,14 @@ in the program.
319319
If a local object in your function is stored in the stack, you should never
320320
return a pointer to this local object from the function. Because
321321
this pointer will always become undefined after the function returns, since the stack space of the function
322-
is destroyed at the end of it's scope.
322+
is destroyed at the end of its scope.
323323
:::
324324

325325
But what if you really need to use this local object in some way after your function returns?
326326
How can you do this? The answer is: "in the same you would do if this was a C or C++ program. By returning
327327
an address to an object stored in the heap". The heap memory have a much more flexible lifecycle,
328328
and allows you to get a valid pointer to a local object of a function that already returned
329-
from it's scope.
329+
from its scope.
330330

331331

332332
### Heap {#sec-heap}
@@ -344,7 +344,7 @@ and that serves (or "deals with") any incoming request that reaches this particu
344344
The heap is a good choice for this type of system, mainly because the server does not know upfront
345345
how many requests it will receive from users, while it is active. It could be one single request,
346346
or, 5 thousand requests, or, it could also be zero requests.
347-
The server needs to have the ability to allocate and manage it's memory according to how many requests it receives.
347+
The server needs to have the ability to allocate and manage its memory according to how many requests it receives.
348348

349349
Another key difference between the stack and the heap, is that the heap is a type
350350
of memory that you, the programmer, have complete control over. This makes the heap a
@@ -379,13 +379,9 @@ In summary, the Zig compiler will use the following rules to decide where each
379379
object you declare is stored:
380380

381381
1. every literal value (such as `"this is string"`, `10`, or `true`) is stored in the global data section.
382-
383382
1. every constant object (`const`) whose value **is known at compile-time** is also stored in the global data section.
384-
385383
1. every object (constant or not) whose length/size **is known at compile time** is stored in the stack space for the current scope.
386-
387384
1. if an object is created with the method `alloc()` or `create()` of an allocator object, this object is stored in the memory space used by this particular allocator object. Most of allocators available in Zig use the heap memory, so, this object is likely stored in the heap (`FixedBufferAllocator()` is an exception to that).
388-
389385
1. the heap can only be accessed through allocators. If your object was not created through the `alloc()` or `create()` methods of an allocator object, then, he is most certainly not an object stored in the heap.
390386

391387

@@ -400,13 +396,13 @@ source code of these operators, and find the memory allocation calls.
400396
Many programmers find this behaviour annoying and hard to keep track of.
401397

402398
But, in Zig, if a function, an operator, or anything from the standard library
403-
needs to allocate some memory during it's execution, then, this function/operator needs to receive (as input) an allocator
399+
needs to allocate some memory during its execution, then, this function/operator needs to receive (as input) an allocator
404400
provided by the user, to actually be able to allocate the memory it needs.
405401

406402
This creates a clear distinction between functions that "do not" from those that "actually do"
407403
allocate memory. Just look at the arguments of this function.
408-
If a function, or operator, have an allocator object as one of it's inputs/arguments, then, you know for
409-
sure that this function/operator will allocate some memory during it's execution.
404+
If a function, or operator, have an allocator object as one of its inputs/arguments, then, you know for
405+
sure that this function/operator will allocate some memory during its execution.
410406

411407
An example is the `allocPrint()` function from the Zig standard library. With this function, you can
412408
write a new string using format specifiers. So, this function is, for example, very similar to the function `sprintf()` in C.
@@ -464,8 +460,7 @@ known fixed length.
464460
But in reality, there are two very common instances where this "fixed length limitation" of the stack is a deal braker:
465461

466462
1. the objects that you create inside your function might grow in size during the execution of the function.
467-
468-
2. sometimes, it is impossible to know upfront how many inputs you will receive, or how big this input will be.
463+
1. sometimes, it is impossible to know upfront how many inputs you will receive, or how big this input will be.
469464

470465
Also, there is another instance where you might want to use an allocator, which is when you want to write a function that returns a pointer
471466
to a local object. As I described at @sec-stack, you cannot do that if this local object is stored in the
@@ -499,7 +494,7 @@ allocators available in the standard library:
499494
- `c_allocator()` (requires you to link to libc).
500495

501496

502-
Each allocator have it's own perks and limitations. All allocators, except `FixedBufferAllocator()` and `ArenaAllocator()`,
497+
Each allocator have its own perks and limitations. All allocators, except `FixedBufferAllocator()` and `ArenaAllocator()`,
503498
are allocators that use the heap memory. So any memory that you allocate with
504499
these allocators, will be placed in the heap.
505500

Chapters/01-zig-weird.qmd

+17-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ knitr::opts_chunk$set(
2121

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

@@ -203,12 +203,11 @@ In this `root.zig` module, we are declaring a function called `add()`, which has
203203
The function returns an integer of the type `i32` as result.
204204

205205

206-
Zig is not exactly a strongly-typed language. Because you can (if you want to) omit
207-
the type of an object in your code, if this type can be derived from the assigned value.
208-
But there are other situations where you do need to be explicit.
206+
Zig is a strongly-typed language. There are some specific situations where you can (if you want to) omit
207+
the type of an object in your code, if this type can be inferred by the `zig` compiler (we talk more
208+
about that at @sec-type-inference). But there are other situations where you do need to be explicit.
209209
For example, you do have to explicitly specify the type of each function argument, and also,
210-
the return type of every function you create in Zig. So, at least in function declarations,
211-
Zig is a strongly-typed language.
210+
the return type of every function that you create in Zig.
212211

213212
We specify the type of an object or a function argument in Zig by
214213
using a colon character (`:`) followed by the type after the name of this object/function argument.
@@ -633,7 +632,7 @@ t.zig:4:11: error: unused local constant
633632
Everytime you declare a new object in Zig, you have two choices:
634633

635634
1. you either use the value of this object;
636-
2. or you explicitly discard the value of the object;
635+
1. or you explicitly discard the value of the object;
637636

638637
To explicitly discard the value of any object (constant or variable), all you need to do is to assign
639638
this object to an special character in Zig, which is the underscore (`_`).
@@ -1001,13 +1000,13 @@ your source code. In less technical terms, blocks are used to specify where in y
10011000
you can access whatever object you have in your source code.
10021001

10031002
So, a block is just a group of expressions contained inside a pair of curly braces.
1004-
And every block have it's own scope separated from the others.
1003+
And every block have its own scope separated from the others.
10051004
The body of a function is a classic example of a block. If statements, for and while loops
10061005
(and any other structure in the language that uses the pair of curly braces)
10071006
are also examples of blocks.
10081007

10091008
This means that, every if statement, or for loop,
1010-
etc., that you create in your source code have it's own separate scope.
1009+
etc., that you create in your source code have its own separate scope.
10111010
That is why you can't access the objects that you defined inside
10121011
of your for loop (or if statement) in an outer scope, i.e. a scope outside of the for loop.
10131012
Because you are trying to access an object that belongs to a scope that is different
@@ -1131,7 +1130,8 @@ where exactly the array ends, or, in other words, to find how much elements the
11311130
To do that, you would need something like this in C. In this example, the C string stored in
11321131
the object `array` is 25 bytes long:
11331132

1134-
```c
1133+
```{c}
1134+
#| eval: false
11351135
#include <stdio.h>
11361136
int main() {
11371137
char* array = "An example of string in C";
@@ -1200,7 +1200,10 @@ const stdout = std.io.getStdOut().writer();
12001200
pub fn main() !void {
12011201
const string_object = "This is an example of string literal in Zig";
12021202
const simple_array = [_]i32{1, 2, 3, 4};
1203-
try stdout.print("Type of array object: {}", .{@TypeOf(simple_array)});
1203+
try stdout.print(
1204+
"Type of array object: {}",
1205+
.{@TypeOf(simple_array)}
1206+
);
12041207
try stdout.print(
12051208
"Type of string object: {}",
12061209
.{@TypeOf(string_object)}
@@ -1242,7 +1245,9 @@ const std = @import("std");
12421245
const stdout = std.io.getStdOut().writer();
12431246
pub fn main() !void {
12441247
const string_object = "Ⱥ";
1245-
try stdout.print("Bytes that represents the string object: ", .{});
1248+
_ = try stdout.write(
1249+
"Bytes that represents the string object: "
1250+
);
12461251
for (string_object) |char| {
12471252
try stdout.print("{X} ", .{char});
12481253
}

0 commit comments

Comments
 (0)