Zig is a fast and highly performant low-level, compiled, statically typed systems programming language developed to be the ultimate replacement for C. Zig claims to be faster and more performant than C, now that's quite some big shoes to fit. Zig was designed by Andrew Kelley. The very first release of the Zig compiler was in March 2016. Zig is still under active development, but it has been used to build some awesome apps. The Bun Javascript runtime is an excellent example of what can be accomplished with Zig.
Zig is not all out against C as you can compile your Zig code into C if you ever need to do that. You can also include C header files in your Zig projects. Zig attempts to use most of the existing concepts and it also maintains a syntax that is familiar to developers. Zig is designed to be simple to use and much of the focus is put on helping you as a developer to debug your application as opposed to "debugging your programming language". This a term that I find very funny because sadly that's what It is most of the time.
In today's post, we will explore 6 main reasons why Zig will be the ultimate killer of C. Here are the main talking points;
• Syntax
• Memory Management Style
• Out-of-the-box support for cross-compiling
• Performance & Safety
• First-class support for test-driven development.
• Seamless Integration with C
• Why should you care?
Syntax
Zig is designed to be easy to learn and use from a beginner's perspective. If you've used any C-like language then the learning curve for Zig is significantly reduced. I downloaded and extracted the Zig compiler, added it to the PATH, and set up and ran my first Hello World program in less than 10 minutes. This is in part because I downloaded the wrong compiler for my computer twice. This just goes to show you how straightforward it is to set up and start a zig project. Let's see some basic Zig syntax with a Hello World program.
const std = @import("std");
pub fn main() !void {
std.debug.print("Hello, World!\n", .{});
}
// Hello World!
Zig tries to avoid most of the garbage that spills into our program when working with languages like C or Go, let's say we have a situation.
a = b + c
But c
is a function that is masquerading as a variable, and this is one of the sources of bugs in apps. In Zig, if something is a function it has to be called when you use it.
Memory Management Style
Zig has a unique way of handling memory management, In a C program you are responsible for manually allocating and freeing up used memory with the malloc and free function, this can potentially lead to bugs in your code because you can free memory which is still in use, or memory which you have already freed. Zig has a different approach, Zig provides the defer keyword that allows you to delay the memory deallocation to the end of your process.
In Zig, a pointer is not allowed to point to nothing, this is in stark contrast to C, where you can have empty pointers, Instead, Zig makes use of option types which is just separate type used to indicate data that is optionally empty. This can be compared to using a structure that contains a pointer and a boolean that determines whether the pointer is valid, but the boolean's state is implicitly managed by the language rather than having to be explicitly managed by the programmer. Zig's use of option types is one of the things that allows it to achieve high performance. By using option types, Zig is able to manage memory efficiently without adding any significant overhead to the program.
Out-of-the-box support for cross-compiling
Zig is built with cross-compiling in mind as such it allows you to compile your code to target different architectures and operating systems. Zig also includes a number of tools that make it easier to cross-compile, such as the zig targets
command and the zig cc
command. The zig targets
command lists all of the target platforms that Zig supports. The zig cc
command is a drop-in replacement for the C compiler that can be used to cross-compile C and C++ code. Zig's out-of-the-box support for cross-compiling makes it easy to develop software for multiple platforms from a single codebase with little to no configurations.
This allows Zig programs to be portable because you don't need to install a separate compiler for each platform you are targeting, the Zig compiler has already taken care of that for you. This also allows you to test your software on multiple platforms without having to install it on each platform. The Zig compiler can also optimize the code for the target platform.
Performance & Safety
Zig is designed with both performance and safety in mind, and thus it provides four different build modes to compile your code;
• Debug mode - The Debug mode is optimized for safety. It disables all of Zig's compiler optimizations and enables all of Zig's safety checks. The Debug mode is characterized by fast compilation speed, slow runtime performance, and a large binary size.
• ReleaseFast - The ReleaseFast mode is optimized for performance. It enables all of Zig's compiler optimizations and disables all of Zig's safety checks. It is characterized by slow compilation speeds, fast runtime performance, and a large binary size.
Large binary size
• ReleaseSafe - The ReleaseSafe mode is optimized for safety and performance. It enables some of Zig's compiler optimizations and disables some of Zig's safety checks. It is characterized by having a medium runtime performance, slow compilation speed, and a large binary size.
• ReleaseSmall - The ReleaseSmall mode is optimized for size. It enables some of Zig's compiler optimizations and disables some of Zig's safety checks. It is characterized by having medium runtime performance, slow compilation speed, and a small binary size.
First class for test-driven development.
Zig ships with a standard testing library that enables you to test your code. Zig's std.testing
test module is an effective tool that makes it simple to write and execute unit tests. It offers several features and a variety of test writing techniques, such as the use of the expect function, the try keyword, and descriptive test names. Being able to group tests into suites. The capability of parallel testing, the capacity to produce reports on test coverage. It is simple to use the std.testing
module to write tests here's an example of a zig test.
import std.testing;
test "expect addOne adds one to 41" {
try std.testing.expect(addOne(41) == 42);
}
fn addOne(number: i32) i32 {
return number + 1;
}
Seamless Integration with C
Zig offers seamless integration with C by allowing you to use Zig to call C functions and libraries, and you can also use C code in your Zig programs. Zig also allows you to import C header files. Zig's seamless integration with C is made possible because Zig uses the same ABI (Application Binary Interface) as C. This means that Zig code can be compiled to the same machine code as C code and that Zig code can call C functions directly without having to go through a wrapper. This allows you to port existing C code to zig without much stress and it also reduces the learning curve significantly for developers who are already familiar with C or any C family of languages
Why should you care?
If you are a programmer looking to learn a systems language then I'm going to suggest that you definitely check out Zig, why? Because Zig is a very fast and highly performant language that allows you to cross-compile your applications to target different platforms from a single codebase without any extra setup.
If you already using C this does not mean that you shouldn't care because Zig was meant to replace C! Zig is better than C in terms of performance, safety, and memory management. It also prevents you from shooting yourself in the foot the way C does. I hope you found this interesting definitely leave your thoughts about Zig in the comment section, do you think that it will replace C? Do you consider Zig to be better than C or do you think that C is just too OG to be touched by this imposter? I will see you in the next one.