×

Wir verwenden Cookies, um LingQ zu verbessern. Mit dem Besuch der Seite erklärst du dich einverstanden mit unseren Cookie-Richtlinien.

Programming, Zig Programming Language | Thoughts After 2 Ye… – Text to read

Programming, Zig Programming Language | Thoughts After 2 Years

Mittelstufe 1 Englisch lesson to practice reading

Beginne jetzt mit dieser Lektion

Hi there!

I'm a big fan of the Zig programming language and today I want to show you why I love it and why you could love it too!

And spoiler is "It's not the 'comptime'."

So, reason number one is its error handling.

Let's start with an example of a small C program.

Here we have our main function.

Let's just say we want to open a file.

Then we want to read its content.

We will need a buffer for that.

So, let us allocate that onto the heap.

And now we can read the content into the buffer.

Finally, let us add a comparison to see what we've just read.

If the read value matches, we want to print a message and return zero as our exit code.

Otherwise we want to return one.

Looks good, right?

Except that it's not good at all.

In just 12 lines of code we have 5 places where the code can fail.

See for yourself:

The first error is opening a file.

It can fail if the file doesn't exist or we don't have enough permissions.

Currently our code will receive a null pointer and happily continue execution resulting in a SEGFAULT later. See "C Programming in a Nutshell" for references.

Secondly, when we allocate memory, the OS (Operating System) could just answer us "No."

Then we get a null pointer again, continue execution and again SEGFAULT!

Third, reading the file's content cannot quite fail but it returns the number of bytes read, which could be less than we expected.

For example, for an empty file, it will just return 0 and never modify the buffer.

In this case, the buffer will just contain garbage, which we will happily compare with our password and probably get a security breach.

Fourth and fifth: we allocate resources, which are the file descriptor and a memory chunk, but we never free them.

This is a resource leak.

We need to close the file and free the memory in two places: here and here.

That's a lot of code already and we have not even started to handle the errors!

In Zig we can do much better.

Let us start with the main function again.

Then, let's open a file, like this.

It's a bit more verbose this time.

But now everything is explicit since the file is now a special type, one that records possible errors.

And we cannot read this file now, but we need to process the error first or we will get a compile time error.

One option here is to use the `try` keyword like this.

This way, in case of an error, the function will likely return early and, in this case, main function will finish with an error.

It will print exactly what went wrong instead of the undefined behavior.

Another option is to use the `catch` keyword like this.

Now we should specify what we want to do in case of an error.

We can finish the program early here with a non-zero exit code or we can reproduce the C behavior, which is which is just pretending that the error can never happen.

Or we can do something else like print the error message and call the exit function.

We can even get the error here and do something different depending on what exactly went wrong.

Every time we'll get a lot of help from the language it's because of Zig's type system.

Okay, let us say the desired behavior is to finish the program with an error message in case of an error.

So, let's return to the `try` keyword.

Let's now allocate a memory block for the buffer.

This time we allocate memory like in C but 2 things are different.

For one, we explicitly specify what allocator we want to use instead of relying on a global state.

And secondly, again, we have to handle an error here or the compiler will not let us build the program.

So, let's assume that, in case of an allocation error, we want to panic like this.

So now, we have a guaranteed open file in allocated memory and we can read the file content into the buffer.

Let's read the file content now.

This time, unlike in C, we cannot just ignore the amount of bytes read.

We will have to use it or explicitly discard the value.

Also, this time we are getting again an error here.

So, in case we will get zero bytes, we'll know if it happened because of an empty file or because of some permission error like we were trying to read a directory.

So now we have everything we need.

Let's check the file's content and let's set a nonzero exit code in case of a wrong password just like before.

Okay, so now, the code is pretty straightforward but it processes all possible errors, is very explicit about what it does, and is really hard to mess up.

We still have to free memory and close the file.

But, remember, we need to do it in every possible execution path.

So, in every case we return from this function with the `try` keyword too!

To simplify our life, in Zig we have a `defer` keyword for resource cleanup.

Let's use it here to close the file and to free memory.

Now our code is not only safe and robust, it also correctly manages resources and it's even shorter than the C version, which just considered happy path and only manages resources.

Compared to some high level languages like Python, Zig is still offering very robust and explicit ways to handle errors.

Second reason for why I love Zig is its explicitness and control.

We already saw how Zig is explicit but let me show you where it could be important.

Consider the following simple Python code.

We are printing a log message to the console.

This isn't a good way of doing it for many reasons but it's mainly because that this function is trading ease of use for control and it's making many decisions for us in this simple case.

Let's break it down and see what decisions are made for us.

First of all, this function writes to `stdout` and for logs it's usually not what we want and we would like to use `stderr` instead.

Secondly this function does something we did not explicitely asked for.

It adds a new line character, which is always backshash `n` and it may not be a desired behavior, for example on Windows, where new line is encoded with backslash `r`, backslash `n`.

Thirdly, this function is hiding some details that could be important in some cases.

So, let's check out this version.

We're printing part of a message to the console, then waiting for a second, then printing another part, then waiting for another second, and finally ending the message with the last part and a new line.

If you run this code, you will probably be surprised since it will do nothing for 2 seconds then print the whole message at once.

This is because the print function in Python is doing hidden buffering for us, and in some cases like this it's not the desired behavior.

And finally, in the case of an error, the print function could fail with exception, and and it's not always what we want.

It's more probable that we would like to just ignore the error or we could write it to another stream.

In Zig, all of these problems are not problems you will face someday in production.

It's all explicitly here, right when you write your code.

For reasons we already discussed, Zig doesn't have a print function in the standard library.

Well, actually you can use `std.debug.print`, which provides ease of use strictly for debugging purposes.

But it's easy to see that it's not something you should leave in your production code.

In other cases, all the decisions are ours to make.

To write to the console, we will first need to open its file descriptor like this and use its writer interface.

Here we are explicitly making the decision if we want to write to `stdout` or `stderr`.

After we explicitly specify the line ending we want to use and what we want to do in case of an error, no extra work is done for us.

So, if we want to buffer the output, we will have to use the buffered writer explicitly, like this.

In the case of doing quick R&D and not wanting to bother with the details but to just write robust production code, Zig is the way to go.

Remark: Usually, R&D stands for "Research and Development." However, in developer contexts, "quick R&D" often means informal, fast-paced investigation or prototyping — testing out approaches, evaluating languages or tools, and getting a feel for feasibility, rather than full-scale scientific or product development work.

Okay, let's consider another example.

Consider this function in Rust.

Here we're creating a list, then adding some elements to it.

Rust is already a very explicit well-designed language but even here we have some problems that Zig solves.

First of all, these lines [actually, just the second] can fail.

Here, Rust is doing implicit memory allocations [again, only on the second line], and this code could probably straight up panic closing our program even if we don't want it to.

Secondly, these implicit allocations are hiding performance pitfalls from us.

Let's see how it would be in the case of Zig.

Even in signature, we can see that this function could just fail and we will have to process it on the site call instead of just panicking.

In the body we will have to again explicitly specify what allocator we want to use.

Now, adding elements to the dynamic array, we can see that operation could fail and it could be an easy indicator of costly reallocation.

So, it's easy to notice what we could do different here.

And, in this case, we should pre-allocate the whole array like this.

And now we can also use another append function, the one that assumes that we have enough space in the array, so it will not fail, and it's faster due to removed checks.

So, in this case again the explicitness of Zig helped us make better decisions and write more robust and performant code.

So, the second thing I love about Zig is its tooling.

Okay, it's not quite that good but it does have two very nice features.

First, it has built-in unit tests, which are just very nice to have in a language.

The test name could be any string with spaces and I do miss this in Rust and Python. [I mean part with arbitary test names.]

And there's no need for extra dependencies: simple Zig build test is enough.

Another thing is Zig's cross compilation.

Compile from any platform to any platform without any extra steps and dependencies.

And, after using other compiled languages, this just feels like magic.

And, of course, Zig has a build system, which is just straight up Zig code, which frees us from learning another language or system and makes build processes as powerful as the language itself.

Okay, I'm not a big fan of other Zig tools, especially the Zig language server.

It could be better but overall Zig tooling is simple and robust, just like the language itself.

Okay. So, the next thing I love about Zig is its design.

Every design decision in Zig is made with a good reason behind it, sometimes through many trials and errors.

And Zig isn't afraid to be the first to try something new.

For example, let's check out this constant definition.

So, we have the `const` keyword, then the name, and then its value after the equal sign.

Now we check out how to define an `enum`.

It's again the `const` keyword, then name, then a block with possible enumerable values.

Okay, now check out how we define a `struct`: again `const`, name, name, and now a block with fields.

What about a module import?

`const` again, then name, then import builtin to get the whole module value.

The only thing what's different is the functions.

I would be happier if they were also constant but there's also a good reason to leave it behind.

Okay. So this consistency, you may say orthogonality, is not only about definition.

If we want to create a `struct` literal we could write: [code, dude].

Notice [that] we could omit the type name in the literal.

Now same with `enum` literals we could write: [code].

And we could omit the type again.

Another good thing about Zig's design decisions is the features it doesn't have.

For example, check out this line of code.

In Zig, we can tokenize this line 3 without knowing anything about what's before or after it.

In Rust, it cannot be an actual `const` declaration but part of a multi-line comment like this: [code].

Or part of a multi-line string literal like this: [more code].

That's one of the reasons why Zig doesn't have multi-line comments and multi-line string literals.

They're not actual multi-line strings but rather something like a bunch of one-line comments.

On the multi-line strings in Zig, let us check them out.

It may look weird at first but it's actually a very good design decision.

First of all, as we already saw, since there is an indicator of a string in the beginning of each line, the tokenizer is stateless.

Secondly, in this example, we have precise control over where we can get new lines and leading spaces, which could be extra important in the case of, for example, YAML files.

Compare it to the same multi-line string in Python and you will see who's a winner here.

Okay. And another example of good Zig design, in my opinion, is actually `async`.

Hear me out: yes, Zig doesn't even have support for `async` / `await` syntax, and it is a good thing.

Implementing it right, especially in a low-level language, is not just hard, it's probably just impossible.

And I'd rather have a good async library without in-language support than a halfbake terrible feature with a lot of hidden complexity magic and performance issues.

Okay. And the last thing I love Zig for is its community.

Most of the people I've met in the Zig community are both pretty experienced, enthusiastic with computers and programming, just like old hackers, and at the same time they're very friendly, helpful and down to earth.

The author of the language, and the core team as well, are genuinely nice guys who are not afraid to be themselves.

So if you're looking for an awesome, chill community to learn something new, Zig is definitely the way to go.

Even if you don't like the language itself, you'll probably learn a lot about computers, operating systems, and programming in general just from Zig being a very low-level and precise language.

And the community will make the learning process enjoyable.

So, you're welcome! 😀

And that's it for today.

I hope you learned something new about Zig.

And, again, this video was voiced by me, Tokisuno. Pay me a visit, if you want to.

Subscribe, if you want more videos like this, and I will see you in the next one!

Learn languages from TV shows, movies, news, articles and more! Try LingQ for FREE