← Zurück zu Lernen

Warum Zig, wenn es bereits C++, D, und Rust gibt?

Kein versteckter Kontrollfluss

Wenn Zig-Code nicht so aussieht als würde er verzweigen, um eine Funktion aufzurufen, dann dann tut er das auch nicht. Das bedeutet, dass man sicher sein kann, dass der folgende Code nur foo() und dann bar() aufruft, und das dies garantiert ist, ohne dass man die Typen von irgendetwas kennen müsste:

var a = b + c.d;
foo();
bar();

Beispiele versteckten Kontrollflusses:

Der Zweck dieser Entscheidung ist die Verbesserung der Lesbarkeit.

Keine versteckten Zuweisungen

Zig verfolgt einen zurückhaltenden Ansatz, wenn es um Speicherzuweisungen geht. Es gibt kein new-Schlüsselwort oder ein anderes Sprachmerkmal, das eine Speicherzuweisung verwendet (z. B. der String-Verkettungsoperator[1]). Das gesamte Konzept der Speicherzuweisung wird von der Bibliothek und dem Anwendungscode verwaltet, nicht von der Sprache.

Beispiele für versteckte Zuweisungen:

In fast allen Sprachen mit automatischer Speicherverwaltung gibt es versteckte Zuweisungen, da der Garbage-Collector die Spuren auf der Aufräumseite verwischt.

Das Hauptproblem bei versteckten Zuweisungen besteht darin, dass sie die Wiederverwendbarkeit eines Codeteils verhindern und die Anzahl der Umgebungen in denen der Code eingesetzt werden kann, unnötig einschränken. Einfach ausgedrückt gibt es Anwendungsfälle, in denen man sich darauf verlassen können muss, dass der Kontrollfluss und die Funktionsaufrufe nicht den Nebeneffekt der Speicherzuweisung aufweisen. Daher kann eine Programmiersprache diese Anwendungsfälle nur dann bedienen, wenn sie diese Garantie realistisch bieten kann.

In Zig gibt es Standardbibliotheksfunktionen, die Speicherzuweisungen bereitstellen und mit ihnen arbeiten, aber das sind optionale Standardbibliotheksfunktionen, die nicht in die Sprache selbst eingebaut sind. Wenn man niemals einen Sspeicherzuweisungs-Allokator initialisiert, kann man sicher sein, dass das Programm auch keine Speicherzuweisungen durchführen wird.

Jede Funktion der Standardbibliothek, die Speicher zuweisen muss, akzeptiert einen Parameter Allocator, um dies zu tun. Das bedeutet, dass die Zig-Standardbibliothek freistehende Ziele unterstützt. Zum Beispiel können std.ArrayList und std.AutoHashMap für direkte Hardware-Programmierung (Bare-Metal) verwendet werden!

Benutzerdefinierte Allokatoren machen die manuelle Speicherverwaltung zum Kinderspiel. Zig hat einen Debug-Allokator, der die Speichersicherheit angesichts von 'use-after-free' und 'double-free' aufrechterhält. Er erkennt automatisch Speicherlecks und gibt davon dann Stack-Traces aus. Es gibt einen Arena-Allokator, mit dem man eine beliebige Anzahl von Zuweisungen zu einer bündeln und diese alle auf einmal freigeben kann, anstatt jede Zuweisung einzeln zu verwalten. Spezielle Allokatoren können verwendet werden, um die Leistung oder die Speichernutzung für die Bedürfnisse einer bestimmten Anwendung zu verbessern.

[1]: Es gibt zwar einen String-Verkettungsoperator (im Allgemeinen ein Array-Verkettungsoperator), aber er funktioniert nur zur Kompilierzeit, d.h. er führt keine Speicherzuweisung zur Laufzeit durch.

Erstklassige Unterstützung für Nicht-Standardbibliotheken

Wie bereits angedeutet verfügt Zig über eine vollständig optionale Standardbibliothek. Jede Standardbibliotheks-API wird nur dann in das Programm kompiliert, wenn sie auch verwendet wird. Zig bietet die gleiche Unterstützung sowohl für das Verknüpfen mit 'libc' als auch für das Nichtverknüpfen damit. Zig ist besonders geeignet für Bare-Metal- und High-Performance-Entwicklung.

Das Beste aus beiden Welten: In Zig können WebAssembly-Programme zum Beispiel die normalen Funktionen der Standardbibliothek nutzen und dennoch die kleinsten Binärdateien erzeugen, im Vergleich zu anderen Programmiersprachen, die ebenfalls das Kompilieren nach WebAssembly unterstützen.

Eine portable Sprache für Bibliotheken

Einer der heiligen Grale der Programmierung ist die Wiederverwendung von Code. Leider erleben wir in der Praxis, dass wir das Rad immer wieder neu erfinden. Oft ist das sogar gerechtfertigt.

Ein Paketmanager und Build-System für bestehende Projekte

Zig ist nicht nur eine Programmiersprache, sondern auch eine Werkzeugsammlung. Sie kommt mit einem Build-System und Paketmanager einher, die auch im Kontext eines traditionellen C/C++-Projekts nützlich sind.

Man kann nicht nur Zig-Code anstelle von C- oder C++-Code schreiben, sondern man kann Zig als Ersatz für Autotools, wie 'cmake', 'make', 'scons', 'ninja', etc. verwenden. Und obendrein bietet es einen Paketmanager für native Abhängigkeiten. Dieses Build-System ist auch dann geeignet, wenn die gesamte Codebasis eines Projekts in C oder C++ vorliegt. Zum Beispiel durch Portierung von 'ffmpeg' auf das Zig-Build-System, wird es möglich, 'ffmpeg' auf jedem unterstützten System für jedes unterstützte System zu kompilieren, indem man nur die knapp 50 MiB von Zig herunterlädt. Für Open-Source-Projekte kann diese vereinfachte Möglichkeit, aus dem Quellcode zu kompilieren - und sogar Cross-Compile - den Unterschied zwischen der Gewinnung oder dem Verlust wertvoller Mitwirkender führen.

System-Paketmanager wie 'apt-get', 'pacman', 'homebrew' und andere, sind für die Verwendung der Endbenutzer gedacht und können daher unzureichend für die Bedürfnisse von Entwicklern sein. Ein sprachspezifischer Paketmanager kann der Unterschied sein, zwischen keinen oder vielen Mitwirkenden. Bei Open-Source-Projekten besteht die Schwierigkeit oft darin, das Projekt überhaupt zum Bauen zu bringen, eine große Hürde für potenzielle Mitwirkende. Für C/C++ Projekte können Abhängigkeiten fatal sein, besonders unter Windows, wo es keinen Paketmanager gibt. Selbst um nur Zig zu bauen, haben die meisten potenziell Mitwirkenden Schwierigkeiten mit der LLVM-Abhängigkeit. Zig bietet eine Möglichkeit, wie Projekte von nativen Bibliotheken abhängen können, ohne davon abhängig zu sein, dass der System-Paketmanager des Benutzers die richtige Version zur Verfügung stellt. So wird praktisch garantiert, dass Projekte beim ersten Versuch erfolgreich gebaut werden, ganz gleich, welches System verwendet wird und unabhängig davon, welche Plattform anvisiert wird.

Andere Sprachen haben zwar Paketmanager, aber sie beseitigen keine lästigen Systemabhängigkeiten wie Zig.

Zig kann das Build-System eines Projekts durch eine vernünftige Sprache ersetzen, die eine deklarative API zum Erstellen von Projekten verwendet. Diese bietet auch ein Paketmanagement und damit die Möglichkeit, tatsächlich von anderen C-Bibliotheken abhängig zu sein. Die Fähigkeit, Abhängigkeiten zu verwalten, ermöglicht höhere Abstraktionen und damit die Weiterverbreitung wiederverwendbaren, hochertigen Codes.

Einfachheit

C++, Rust und D haben so viele Funktionen, dass sie vom eigentlichen Sinn der Anwendung ablenken können, an der man gerade arbeitet. Man ertappt sich dabei, seine Kenntnisse der Programmiersprache zu debuggen, anstatt die Anwendung selbst zu debuggen.

Zig hat keine Makros, ist aber dennoch leistungsstark genug, um komplexe Programme auf eine klare, nicht repetitive Weise auszudrücken. Sogar Rust hat Makros mit Sonderfällen wie format!, das im Compiler selbst implementiert ist. In Zig hingegen ist die äquivalente Funktion in der Standardbibliothek implementiert, ohne Sonderfall-Code im Compiler.

Werkzeuge

Zig kann im Downloadbereich heruntergeladen werden. Zig bietet Binärarchive für Linux, Windows und macOS. Im Folgenden wird beschrieben, was man mit einem dieser Archive erhält: