Zig (programming language)

From HandWiki
Short description: A general-purpose programming language, toolchain to build Zig/C/C++ code
Zig
Zig logo 2020.svg
ParadigmsMulti-paradigm: imperative, concurrent, procedural, functional
Designed byAndrew Kelley
First appeared8 February 2016; 8 years ago (2016-02-08)[1]
Typing disciplineStatic, strong, inferred, structural, generic
Platformx86-64, ARM64, WebAssembly
Tier 2: ARM, IA-32, RISC-V, MIPS64, POWERPC64, SPARC64, some tier-2 platforms have tier-1 support for standalone programs
OSCross-platform: Linux, FreeBSD, Windows
LicenseMIT
Filename extensions.zig, .zir
Websiteziglang.org
Influenced by
C, C++, LLVM IR, Go, Rust, JavaScript[citation needed]

Zig is an imperative, general-purpose, statically typed, compiled system programming language designed by Andrew Kelley. It is intended to be a successor to the C programming language, with the goals of being even smaller and simpler to program in while also offering modern features, new optimizations and a variety of safety mechanisms while not as demanding of runtime safety as seen in other languages.[2] It is distinct from languages like Go, Rust and Carbon, which have similar goals but also target the C++ space.[3][4][5]

The improvements in language simplicity relate to flow control, function calls, library imports, variable declaration and Unicode support. Additionally, the language does not make use of macros or preprocessor instructions. Features adopted from modern languages include the addition of compile-time generic types, allowing functions to work on a variety of data, along with a small set of new compiler directives to allow access to the information about those types using reflection.

Another set of additions to Zig is intended to improve code safety. Like C, Zig does not include garbage collection and memory handling is manual. To help eliminate the potential errors that arise in such systems, it includes option types and simple syntax for using them. A testing framework is also designed into the language.

Description

Goals

The primary goal of Zig is to "be pragmatic", in that it is intended to be a better solution to the sorts of tasks that are currently solved with C. A primary concern in that respect is readability; Zig attempts to use existing concepts and syntax wherever possible, avoiding the addition of different syntax for similar concepts. Additionally, it is designed for "robustness, optimality and maintainability", including a variety of features to improve safety, optimization and testing. The small and simple syntax is an important part of the maintenance, as it is a goal of the language to allow maintainers to debug the code without having to learn the intricacies of a language they might not be familiar with.[6] Even with these changes, Zig can compile into and against existing C code; C headers can be included in a Zig project and their functions called, and Zig code can be linked into C projects by including the compiler-built headers.[7]

In keeping with the overall design philosophy of making the code simple and easy to read, the Zig system as a whole also encompasses a number of stylistic changes compared to C and other C-like languages. For instance, the Rust language has operator overloading which means a statement like a = b + c might actually be a function call to a type's overloaded version of the plus operator. Additionally, that function might raise an exception which might pre-empt any following code. In Zig, if something calls a function, it looks like a function call, if it doesn't, it doesn't look like a function. If it raises an error, it is explicit in the syntax,[7] error handling is handled through error types and can be handled with catch or try.

The goals of Zig are in contrast to those of many other languages designed in the same time period, like Go, Rust, Carbon, and Nim. Generally, these languages are more complex with additional features like operator overloading, functions that masquerade as values (properties), and many other features intended to aid the construction of large programs. These sorts of features have more in common with C++'s approach, and these languages are more along the lines of that language.[7] Zig has a more conservative extension of the type system, supporting compile-time generics and accommodating a form of duck typing with the comptime directive.

Memory handling

One of the primary sources of bugs in C programs is the memory management system, based on malloc. malloc sets aside a block of memory for use in the code and returns a reference to that memory as a pointer. There is no system to ensure that memory is released when the program no longer needs it, which can lead to programs using up all available memory, a memory leak. More common, and sometimes called the worst mistake in computer programming,[8][9] is an invalid pointer due to a null pointer error (when the malloc failed) or a dangling pointer error (when the memory was released by other code, or a host of other problems[10]).

A common solution to these problems is a garbage collector (GC), which examines the program for pointers to previously malloced memory, and removing any blocks that no longer have anything pointing to them. Although this greatly reduces, or even eliminates, memory errors, GC systems are relatively slow compared to manual memory management, and have unpredictable performance that makes them unsuited to systems programming. Another solution is automatic reference counting (ARC), which implements the same basic concept of looking for pointers to removed memory, but does so at malloc time by recording the number of pointers to that block, meaning there does not need to perform an exhaustive search, but instead adds time to every malloc and release operation.[10]

Zig aims to provide performance similar or better than C, so GC and ARC are not suitable solutions. Instead, it uses a modern, (As of 2022), concept known as option types. Instead of a pointer being allowed to point to nothing, or nil, a separate type is used to indicate data that is optionally empty. This is similar to using a structure with a pointer and a boolean that indicates whether the pointer is valid, but the state of the boolean is invisibly managed by the language and does not need to be explicitly managed by the programmer. So, for instance, when the pointer is declared it is set to "unallocated", and when that pointer receives a value from a malloc, it is set to "allocated" if the malloc succeeded.[11]

The advantage to this model is that it has very low or zero overhead; the compiler has to create the code to pass along the optional type when pointers are manipulated, as opposed to a simple pointer, but this allows it to directly express possible memory problems at compile time with no runtime support. For instance, creating a pointer with a null value and then attempting to use it is perfectly acceptable in C, leading to null-pointer errors. In contrast, a language using optional types can check that all code paths only attempt to use pointers when they are valid. While this does not eliminate all potential problems, when issues do occur at runtime the error can be more precisely located and explained.[12]

Another change for memory management in Zig is that the actual allocation is handled through structs describing the action, as opposed to calling the memory management functions in libc. For instance, in C if one wants to write a function that makes a string containing multiple copies of another string, the function might look like this:

const char* repeat(const char* original, size_t times);

In the code, the function would examine the size of original and then malloc times that length to set aside memory for the string it will build. That malloc is invisible to the functions calling it, if they fail to later release the memory, a leak will occur. In Zig, this might be handled using a function like:

fn repeat(allocator: *std.mem.Allocator, original: []const u8, times: usize) std.mem.Allocator.Error![]const u8;

In this code, the allocator variable is passed a struct that describes what code should perform the allocation, and the repeat function returns either the resulting string or, using the optional type as indicated by the !, an Allocator.Error. By directly expressing the allocator as an input, memory allocation is never "hidden" within another function, it is always exposed to the API by the function that is ultimately calling for the memory to be allocated. No allocations are performed inside Zig's standard library. Additionally, as the struct can point to anything, one can use alternative allocators, even ones written in the program. This can allow, for instance, small-object allocators that do not use the operating system functions that normally allocate an entire memory page.[13]

Optional types are an example of a language feature that offers general functionality while still being simple and generic. They do not have to be used to solve null pointer problems, they are also useful for any type of value where "no value" is an appropriate answer. Consider a function countTheNumberOfUsers that returns an integer, and an integer variable, theCountedUsers that holds the result. In many languages, a magic number would be placed in theCountedUsers to indicate that countTheNumberOfUsers has not yet been called, while many implementations would just set it to zero. In Zig, this could be implemented as an var theCountedUsers: ?i32 = null which sets the variable to a clear "not been called" value.[13]

Another more general feature of Zig that also helps manage memory problems is the concept of defer, which marks some code to be performed at the end of a function no matter what happens, including possible runtime errors. If a particular function allocates some memory and then disposes of it when the operation is complete, one can add a line to defer a free to ensure it is released no matter what happens.[13]

Zig memory management avoids hidden allocations. Allocation is not managed in the language directly. Instead, heap access is done in a standard library, explicitly.[14]

Direct interaction with C

Zig promotes an evolutionary approach to using the language that combines new Zig code with existing C code. To do this, it aims to make interaction with existing C libraries as seamless as possible. Zig imports its own libraries with the @import directive, typically in this fashion:

const std = @import("std");

Zig code within that file can now call functions inside std, for instance:

std.debug.print("Hello, world!\n", .{});

To work with C code, one simply replaces the @import with @cImport:

const c = @cImport(@cInclude("soundio/soundio.h"));

The Zig code can now call functions in the soundio library as if they were native Zig code. As Zig uses new data types that are explicitly defined, unlike C's more generic int and float, a small number of directives are used to move data between the C and Zig types, including @intCast and @ptrCast.[13]

Cross compiling

Zig treats cross-compiling as a first-class use-case of the language. This means any Zig compiler can compile runnable binaries for any of its target platforms, of which there are dozens. These include not only widely-used modern systems like ARM and x86-64, but also PowerPC, SPARC, MIPS, RISC-V and even the IBM z/Architectures (S390). The toolchain can compile to any of these targets without installing additional software, all the needed support is in the basic system.[13]

Comptime

By using the comptime keyword, the programmer can explicitly have Zig evaluate sections of code at compile time, as opposed to runtime. Being able to run code at compile time allows Zig to have the functionality of macros and conditional compilation without the need for a separate preprocessor language.[15]

During compile time, types become first-class citizens. This enables compile-time duck typing, and is how Zig implements generic types.[16]

For instance, in Zig, a generic linked list type might be implemented using a function like:

fn LinkedList(comptime T: type) type;

This function takes in some type T, and returns a custom struct defining a linked list with that data type.

Other features

Zig supports compile time generics, reflection and evaluation, cross-compiling, and manual memory management.[17] A major goal of the language is to improve on the C language,[15][18] while also taking inspiration from Rust,[19][7] among others. Zig has many features for low-level programming, notably packed structs (structs without padding between fields), arbitrary-width integers[20] and multiple pointer types.[16]

Zig is not just a new language: it also includes a C/C++ compiler, and can be used with either or both languages.

Versions

Since version 0.10 the (new default) Zig compiler is written in the Zig programming language, i.e., it is a self-hosting compiler, and that is a major new feature of that release. The older legacy bootstrapping compiler, written in C++, is still an option but will not be in version 0.11. When compiling with the new Zig compiler much less memory is used and it compiles a bit faster. The older, now legacy, C++ based compiler uses 3.5x more memory.

Zig's default backend for optimization is still LLVM,[21] and LLVM is written in C++. The Zig compiler with LLVM is 169 MiB, vs without LLVM 4.4 MiB. Faster executable code is usually compiled with the new Zig-lang based compiler, its LLVM code generation is better, and it fixes many bugs, but there are also improvements for the older legacy compiler in version 0.10. The self-hosted linker is tightly coupled with the self-hosted compiler. The new version also adds some experimental (tier-3) support for AMD GPUs (there's also some lesser support for Nvidia GPUs and for PlayStation 4 and 5).

The older bootstrapping ("stage1") compiler is written in Zig and C++, using LLVM 13 as a back-end,[22][23] supporting many of its native targets.[24] The compiler is free and open-source software released under an MIT License.[25] The Zig compiler exposes the ability to compile C and C++ similarly to Clang with the commands zig cc and zig c++,[26] providing many headers including the C standard library (libc) and C++ Standard Library (libcxx) for many different platforms, allowing Zig's cc and c++ sub-commands to act as cross compilers out of the box.[27][28]

Plus the operating systems (mostly desktop ones) officially supported (and documented), (minimal) applications can and have been made for Android (with Android NDK), and programming for iOS also possible.

Zig doesn't have its own official package manager (non-official ones exist), but a standard one has a milestone for 0.12.

Zig development is funded by the Zig Software Foundation (ZSF), a non-profit corporation with Andrew Kelley as president, which accepts donations and hires multiple full-time employees.[29][30][31]

Examples

Hello World

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Hello, {s}!\n", .{"world"});
}

Generic linked list

pub fn main() void {
    var node = LinkedList(i32).Node {
        .prev = null,
        .next = null,
        .data = 1234,
    };

    var list = LinkedList(i32) {
        .first = &node,
        .last = &node,
        .len = 1,
    };
}

fn LinkedList(comptime T: type) type {
    return struct {
        pub const Node = struct {
            prev: ?*Node,
            next: ?*Node,
            data: T,
        };

        first: ?*Node,
        last:  ?*Node,
        len:   usize,
    };
}

String repetition with allocator

const std = @import("std");

fn repeat(allocator: *std.mem.Allocator, original: []const u8, times: usize) std.mem.Allocator.Error![]const u8 {
    var buffer = try allocator.alloc(u8, original.len * times);

    for (0..times) |i| {
        std.mem.copyForwards(u8, buffer[(original.len * i)..], original);
    }

    return buffer;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    var allocator = arena.allocator();

    const original = "Hello ";
    const repeated = try repeat(&allocator, original, 3);

    // Prints "Hello Hello Hello "
    try stdout.print("{s}\n", .{repeated});
}

Projects

See also

References

Citations

  1. Kelley, Andrew. "Introduction to the Zig Programming Language". https://andrewkelley.me/post/intro-to-zig.html. 
  2. "Zig has all the elegant simplicity of C, minus all the ways to shoot yourself in the foot" (in en-US). 2017-10-31. https://jaxenter.com/zig-language-kelley-interview-138517.html. 
  3. Pike, Rob (2012). "Less is exponentially more". http://commandcenter.blogspot.mx/2012/06/less-is-exponentially-more.html. 
  4. Walton, Patrick (2010-12-05). "C++ Design Goals in the Context of Rust". http://pcwalton.blogspot.com/2010/12/c-design-goals-in-context-of-rust.html. 
  5. Carbon Language: An experimental successor to C++ - Chandler Carruth - CppNorth 2022. CppNorth. 2022-07-22 – via YouTube.
  6. Elizabeth 2017.
  7. 7.0 7.1 7.2 7.3 Yegulalp 2016.
  8. Dobrowolski, Mariusz (10 August 2018). "The Worst Mistake in Computer Science". https://softwarehut.com/blog/tech/worst-mistake-computer-science. 
  9. Draper, Paul (8 February 2016). "NULL is the Worst Mistake in Computer Science". https://dzone.com/articles/the-worst-mistake-of-computer-science-1. 
  10. 10.0 10.1 "ARC vs. GC". https://docs.elementscompiler.com/Concepts/ARCvsGC/. 
  11. "Guide To Java 8 Optional". 28 November 2022. https://www.baeldung.com/java-optional. 
  12. "Rust: Memory Management". https://ggbaker.ca/prog-langs/content/rust-memory.html. 
  13. 13.0 13.1 13.2 13.3 13.4 "Allocators". 11 September 2023. https://ziglearn.org/chapter-2/. 
  14. Tyson, Matthew (9 March 2023). "Meet Zig: The modern alternative to C". https://www.infoworld.com/article/3689648/meet-the-zig-programming-language.html. 
  15. 15.0 15.1 The Road to Zig 1.0 - Andrew Kelley. ChariotSolutions. 2019-05-09 – via YouTube.
  16. 16.0 16.1 "Documentation". https://ziglang.org/documentation/master/. 
  17. "The Zig Programming Language". https://ziglang.org/. 
  18. "The Zig Programming Language". https://ziglang.org/#Zig-competes-with-C-instead-of-depending-on-it. 
  19. Company, Sudo Null. "Sudo Null - IT News for you" (in en). https://sudonull.com/post/3683-Zig-programming-language. 
  20. Tim Anderson 24 Apr 2020 at 09:50. "Keen to go _ExtInt? LLVM Clang compiler adds support for custom width integers" (in en). https://www.theregister.co.uk/2020/04/24/llvm_project_adds_support_for/. 
  21. New LLVM version 15, Zig legacy uses version 13
  22. "A Reply to _The Road to Zig 1.0_" (in en-gb). 2019-05-13. https://www.gingerbill.org/article/2019/05/13/a-reply-to-the-road-to-zig/. 
  23. "ziglang/zig". Zig Programming Language. 2020-02-11. https://github.com/ziglang/zig. 
  24. "The Zig Programming Language". https://ziglang.org/#Tier-System. 
  25. "ziglang/zig" (in en). https://github.com/ziglang/zig. 
  26. "0.6.0 Release Notes". https://ziglang.org/download/0.6.0/release-notes.html#zig-cc. 
  27. "'zig cc': a Powerful Drop-In Replacement for GCC/Clang - Andrew Kelley". https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html. 
  28. "Zig Makes Go Cross Compilation Just Work" (in en). 24 January 2021. https://dev.to/kristoff/zig-makes-go-cross-compilation-just-work-29ho. 
  29. "Jakub Konka on Twitter" (in en). https://twitter.com/kubkon/status/1377146321136537602. 
  30. "Announcing the Zig Software Foundation". https://ziglang.org/news/announcing-zig-software-foundation/. 
  31. "Sponsor ZSF". https://ziglang.org/zsf/. 

Bibliography

External links