Zick-Programmiersprache


Der erste Kommentar zu dem wunderbaren Artikel Subjektive Vision einer idealen Programmiersprache stellte sich als Hinweis auf die Zig-Programmiersprache heraus . Natürlich wurde es interessant, um welche Art von Sprache es sich handelt, die behauptet, eine Nische von C ++, D und Rust zu sein. Ich sah aus - die Sprache schien hübsch und etwas interessant. Schöne si-ähnliche Syntax, origineller Ansatz zur Fehlerbehandlung, integrierte Coroutinen. Dieser Artikel gibt einen kurzen Überblick über die offizielle Dokumentation, die mit ihren eigenen Gedanken und Eindrücken aus laufenden Codebeispielen durchsetzt ist.

Erste Schritte


Die Installation des Compilers ist für Windows recht einfach. Entpacken Sie das Distributionspaket einfach in einen Ordner. Wir erstellen eine hello.zig-Textdatei im selben Ordner, fügen den Code aus der Dokumentation dort ein und speichern ihn. Die Montage erfolgt über den Befehl

zig build-exe hello.zig 

Danach erscheint hello.exe im selben Verzeichnis.

Zusätzlich zur Montage steht der Unit-Test-Modus zur Verfügung. Hierzu werden im Code Testblöcke verwendet und die Montage und der Start der Tests werden mit dem Befehl ausgeführt

 zig test hello.zig 

Erste Kuriositäten


Der Compiler unterstützt keine Windows-Zeilenumbrüche (\ r \ n). Die Tatsache, dass Zeilenumbrüche in jedem System (Win, Nix, Mac) einige ihrer eigenen sind, ist natürlich Wildheit und ein Relikt der Vergangenheit. Es ist jedoch nichts zu tun. Wählen Sie beispielsweise in Notepad ++ das gewünschte Format für den Compiler aus.

Die zweite Kuriosität, auf die ich zufällig gestoßen bin - Tabs werden im Code nicht unterstützt! Nur Leerzeichen. Aber es passiert :)

Dies ist jedoch ehrlich in der Dokumentation geschrieben - die Wahrheit ist bereits ganz am Ende.

Kommentare


Eine weitere Kuriosität ist, dass Zig keine mehrzeiligen Kommentare unterstützt. Ich erinnere mich, dass im alten Turbo Pascal alles richtig gemacht wurde - verschachtelte mehrzeilige Kommentare wurden unterstützt. Anscheinend hat seitdem kein Sprachentwickler so eine einfache Sache gemeistert :)

Aber es gibt dokumentarische Kommentare. Beginnen Sie mit ///. Muss an bestimmten Stellen sein - vor den entsprechenden Objekten (Variablen, Funktionen, Klassen ...). Wenn sie woanders sind - ein Kompilierungsfehler. Nicht schlecht.

Variablendeklaration


Geschehen im modischen Jetzt (und ideologisch korrekten) Stil, wenn zuerst das Schlüsselwort (const oder var) geschrieben wird, dann der Name, dann optional der Typ und dann der Anfangswert. Das heißt, Eine automatische Typinferenz ist verfügbar. Variablen müssen initialisiert werden. Wenn Sie keinen Anfangswert angeben, tritt ein Kompilierungsfehler auf. Es wird jedoch ein spezieller undefinierter Wert bereitgestellt, mit dem explizit nicht initialisierte Variablen angegeben werden können.

 var i:i32 = undefined; 

Konsolenausgabe


Für Experimente benötigen wir eine Ausgabe an die Konsole - in allen Beispielen ist dies die verwendete Methode. Im Bereich der Plug-Ins

 const warn = std.debug.warn; 

und der Code ist wie folgt geschrieben:

 warn("{}\n{}\n", false, "hi"); 

Der Compiler hat einige Fehler, die er ehrlich meldet, wenn er versucht, eine Ganzzahl oder eine Gleitkommazahl auf folgende Weise auszugeben:
Fehler: Compiler-Fehler: Integer- und Float-Literale in der Funktion var args müssen umgewandelt werden. github.com/ziglang/zig/issues/557

Datentypen


Primitive Typen


Typnamen stammen anscheinend von Rust (i8, u8, ... i128, u128), es gibt auch spezielle Typen für die binäre C-Kompatibilität, 4 Arten von Gleitkommatypen (f16, f32, f64, f128). Es gibt einen Typ Bool. Es gibt eine Art Nulllücke und eine spezielle Noreturn, auf die ich später noch eingehen werde.

Sie können auch ganzzahlige Typen beliebiger Länge in Bits von 1 bis 65535 erstellen. Der Typname beginnt mit dem Buchstaben i oder u, und dann wird die Länge in Bits geschrieben.

 //  ! var j:i65535 = 0x0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF; 

Dieser Wert konnte jedoch nicht an die Konsole übertragen werden. Während des Kompilierungsvorgangs ist im LLVM ein Fehler aufgetreten.

Im Allgemeinen ist dies eine interessante Lösung, obwohl sie nicht eindeutig ist (IMHO: Die Unterstützung genau langer numerischer Literale auf Compilerebene ist korrekt, aber die Benennung von Typen auf diese Weise ist nicht sehr gut, es ist besser, dies ehrlich über einen Vorlagentyp zu tun). Und warum ist das Limit 65535? Bibliotheken wie GMP scheinen solche Einschränkungen nicht aufzuerlegen?

String-Literale


Dies sind Zeichenarrays (ohne Null am Ende). Für Literale mit einer abschließenden Null wird das Präfix 'c' verwendet.

 const normal_bytes = "hello"; const null_terminated_bytes = c"hello"; 

Wie die meisten Sprachen unterstützt Zig Standard-Escape-Sequenzen und das Einfügen von Unicode-Zeichen durch ihre Codes (\ uNNNN, \ UNNNNNN, wobei N eine hexadezimale Ziffer ist).
Mehrzeilige Literale werden mit zwei umgekehrten Schrägstrichen am Anfang jeder Zeile gebildet. Es sind keine Anführungszeichen erforderlich. Das heißt, einige versuchen, rohe Zeichenfolgen zu erstellen, aber IMHO ist nicht erfolgreich - der Vorteil von rohen Zeichenfolgen besteht darin, dass Sie jeden Text von einer beliebigen Stelle im Code einfügen können - und im Idealfall nichts ändern, aber hier müssen Sie \\ am Anfang jeder Zeile hinzufügen.

 const multiline = \\#include <stdio.h> \\ \\int main(int argc, char **argv) { \\ printf("hello world\n"); \\ return 0; \\} ; 

Ganzzahlige Literale


Alles ist in si-ähnlichen Sprachen. Ich war sehr erfreut, dass für Oktalliterale das Präfix 0o verwendet wird und nicht nur Null, wie in C. Binärliterale mit dem Präfix 0b werden ebenfalls unterstützt. Gleitkomma-Literale können hexadezimal sein (wie in der GCC-Erweiterung ).

Operationen


Natürlich gibt es standardmäßige arithmetische, logische und bitweise C-Operationen. Kurzoperationen werden unterstützt (+ = usw.). Anstelle von && und || die Schlüsselwörter und und oder werden verwendet. Ein interessanter Punkt ist, dass Operationen mit garantierter Wraparound-Semantik zusätzlich unterstützt werden. Sie sehen so aus:

 a +% b a +%= b 

In diesem Fall garantieren gewöhnliche arithmetische Operationen keinen Überlauf und ihre Ergebnisse während des Überlaufs werden als undefiniert betrachtet (und Kompilierungsfehler werden für Konstanten generiert). IMHO ist dies ein wenig seltsam, aber anscheinend wird es aus einigen tiefen Überlegungen zur Kompatibilität mit der Semantik der C-Sprache gemacht.

Arrays


Array-Literale sehen folgendermaßen aus:

 const msg = []u8{ 'h', 'e', 'l', 'l', 'o' }; const arr = []i32{ 1, 2, 3, 4 }; 

Zeichenfolgen sind Anordnungen von Zeichen, wie in C. Klassische Indexierung mit eckigen Klammern. Die Operationen der Addition (Verkettung) und Multiplikation von Arrays werden bereitgestellt. Es ist eine sehr interessante Sache, und wenn bei der Verkettung alles klar ist, dann die Multiplikation - ich habe gewartet, bis jemand dies implementiert, und jetzt warte ich :) In Assembler (!) Gibt es eine solche Dup-Operation, mit der Sie doppelte Daten generieren können. Jetzt in Zig:

 const one = []i32{ 1, 2, 3, 4 }; const two = []i32{ 5, 6, 7, 8 }; const c = one ++ two; // { 1,2,3,4,5,6,7,8 } const pattern = "ab" ** 3; // "ababab" 

Zeiger


Die Syntax ähnelt C.

 var x: i32 = 1234; //  const x_ptr = &x; //   

Für die Dereferenzierung (Werte per Zeiger nehmen) wird eine ungewöhnliche Postfix-Operation verwendet:

 x_ptr.* == 5678; x_ptr.* += 1; 

Der Zeigertyp wird explizit festgelegt, indem ein Sternchen vor dem Typnamen gesetzt wird

 const x_ptr : *i32 = &x; 

Scheiben (Scheiben)


Eine in die Sprache integrierte Datenstruktur, mit der Sie auf ein Array oder einen Teil davon verweisen können. Enthält einen Zeiger auf das erste Element und die Anzahl der Elemente. Es sieht so aus:

 var array = []i32{ 1, 2, 3, 4 }; const slice = array[0..array.len]; 

Es scheint von Go genommen zu sein, nicht sicher. Und ich bin mir auch nicht sicher, ob es sich gelohnt hat, in eine Sprache einzubetten, während die Implementierung einer solchen Sache in eine OOP-Sprache sehr elementar ist.

Strukturen


Eine interessante Möglichkeit, eine Struktur zu deklarieren: Es wird eine Konstante deklariert, deren Typ automatisch als "Typ" (Typ) angezeigt wird und die als Name der Struktur verwendet wird. Und die Struktur selbst (struct) ist "namenlos".

 const Point = struct { x: f32, y: f32, }; 

Es ist unmöglich, einen Namen in C-ähnlichen Sprachen auf die übliche Weise anzugeben. Der Compiler zeigt den Typnamen jedoch nach bestimmten Regeln an. Insbesondere in dem oben betrachteten Fall stimmt er mit dem Namen der Konstante „type“ überein.

Im Allgemeinen garantiert die Sprache nicht die Reihenfolge der Felder und ihre Ausrichtung im Speicher. Wenn Garantien benötigt werden, sollten „verpackte“ Strukturen verwendet werden.

 const Point2 = packed struct { x: f32, y: f32, }; 

Initialisierung - im Stil der Sishny-Bezeichner:

 const p = Point { .x = 0.12, .y = 0.34, }; 

Strukturen können Methoden haben. Beim Platzieren einer Methode in einer Struktur wird die Struktur jedoch einfach als Namespace verwendet. Im Gegensatz zu C ++ werden diese Parameter nicht implizit übergeben.

Transfers


Im Allgemeinen dasselbe wie in C / C ++. Es gibt einige praktische integrierte Methoden für den Zugriff auf Metainformationen, z. B. die Anzahl der Felder und deren Namen, die durch in die Sprache integrierte Syntaxmakros implementiert werden (die in der Dokumentation als integrierte Funktionen bezeichnet werden).

Aus Gründen der "Binärkompatibilität mit C" werden einige externe Aufzählungen bereitgestellt.

Um den Typ anzugeben, der der Aufzählung zugrunde liegen soll, eine Konstruktion des Formulars

 packed enum(u8) 

Dabei ist u8 der Basistyp.
Aufzählungen können strukturähnliche Methoden haben (d. H. Einen Aufzählungsnamen als Namespace verwenden).

Gewerkschaften


Nach meinem Verständnis ist die Vereinigung in Zig eine algebraische Typensumme, d.h. enthält ein verstecktes Tag-Feld, das bestimmt, welches der Vereinigungsfelder "aktiv" ist. Die "Aktivierung" eines anderen Feldes erfolgt durch eine vollständige Neuzuordnung des gesamten Vereins. Dokumentationsbeispiel

 const assert = @import("std").debug.assert; const mem = @import("std").mem; const Payload = union { Int: i64, Float: f64, Bool: bool, }; test "simple union" { var payload = Payload {.Int = 1234}; // payload.Float = 12.34; // !    assert(payload.Int == 1234); //       payload = Payload {.Float = 12.34}; assert(payload.Float == 12.34); } 

Gewerkschaften können auch explizit Aufzählungen für das Tag verwenden.

 // Unions can be given an enum tag type: const ComplexTypeTag = enum { Ok, NotOk }; const ComplexType = union(ComplexTypeTag) { Ok: u8, NotOk: void, }; 

Gewerkschaften können wie Aufzählungen und Strukturen auch einen eigenen Namespace für Methoden bereitstellen.

Optionale Typen


Zig verfügt über eine integrierte optionale Unterstützung. Vor dem Typnamen wird ein Fragezeichen eingefügt:

 const normal_int: i32 = 1234; // normal integer const optional_int: ?i32 = 5678; // optional integer 

Interessanterweise setzt Zig eine Sache um, deren Möglichkeit ich vermutet habe, war mir aber nicht sicher, ob sie richtig war oder nicht. Zeiger werden mit Optionen kompatibel gemacht, ohne dass ein zusätzliches verstecktes Feld („Tag“) hinzugefügt wird, in dem ein Zeichen für die Gültigkeit des Werts gespeichert ist. und null wird als ungültiger Wert verwendet. Daher benötigen die in Zig durch Zeiger dargestellten Referenztypen nicht einmal zusätzlichen Speicher für "Optionalität". Gleichzeitig ist das Zuweisen von Nullwerten zu regulären Zeigern verboten.

Fehlertypen


Sie ähneln optionalen Typen, aber anstelle des Booleschen Tags ("wirklich ungültig") wird ein Aufzählungselement verwendet, das dem Fehlercode entspricht. Die Syntax ähnelt Optionen, anstelle eines Fragezeichens wird ein Ausrufezeichen hinzugefügt. So können diese Typen beispielsweise zur Rückgabe von Funktionen verwendet werden: Entweder wird das Objektergebnis der erfolgreichen Operation der Funktion zurückgegeben, oder es wird ein Fehler mit dem entsprechenden Code zurückgegeben. Fehlertypen sind ein wichtiger Bestandteil des Zig-Sprachfehlerbehandlungssystems. Weitere Informationen finden Sie im Abschnitt Fehlerbehandlung.

Geben Sie void ein


Variablen wie void und Operationen mit ihnen sind in Zig möglich

 var x: void = {}; var y: void = {}; x = y; 

Für solche Operationen wird kein Code generiert. Dieser Typ ist hauptsächlich für die Metaprogrammierung nützlich.

Es gibt auch einen c_void-Typ für C-Kompatibilität.

Bediener und Funktionen steuern


Dazu gehören: Blöcke, Schalter, während, wenn, sonst, brechen, fortfahren. Um den Code zu gruppieren, werden standardmäßige geschweifte Klammern verwendet. Nur Blöcke wie in C / C ++ werden verwendet, um den Umfang von Variablen zu begrenzen. Blöcke können als Ausdrücke betrachtet werden. Es gibt kein goto in der Sprache, aber es gibt Beschriftungen, die mit den Anweisungen break und continue verwendet werden können. Standardmäßig arbeiten diese Operatoren mit Schleifen. Wenn ein Block jedoch eine Beschriftung hat, können Sie diese verwenden.

 var y: i32 = 123; const x = blk: { y += 1; break :blk y; //   blk   y }; 

Die switch-Anweisung unterscheidet sich vom Operator darin, dass sie kein "Fallthrough" hat, d. H. Es wird nur eine Bedingung (Fall) ausgeführt und der Schalter wird beendet. Die Syntax ist kompakter: Anstelle von Groß- und Kleinschreibung wird der Pfeil "=>" verwendet. Switch kann auch als Ausdruck betrachtet werden.

Die while- und if-Anweisungen sind im Allgemeinen dieselben wie in allen C-ähnlichen Sprachen. Die for-Anweisung ähnelt eher foreach. Alle von ihnen können als Ausdrücke betrachtet werden. Von den neuen Funktionen kann while und for sowie if einen else-Block haben, der ausgeführt wird, wenn keine Schleifeniteration vorhanden ist.

Und hier ist es an der Zeit, über eine gemeinsame Funktion für den Switch zu sprechen, die in gewisser Weise aus dem Konzept der foreach-Schleifen entlehnt ist - das „Erfassen“ von Variablen. Es sieht so aus:

 while (eventuallyNullSequence()) |value| { sum1 += value; } if (opt_arg) |value| { assert(value == 0); } for (items[0..1]) |value| { sum += value; } 

Hier ist das while-Argument eine bestimmte „Datenquelle“, die für ein Array oder ein Slice optional sein kann, und eine Variable zwischen zwei vertikalen Linien enthält einen „erweiterten“ Wert, d. H. das aktuelle Element des Arrays oder Slice (oder ein Zeiger darauf), der interne Wert des optionalen Typs (oder ein Zeiger darauf).

Anweisungen verschieben und verschieben


Die von Go geliehene aufgeschobene Ausführungserklärung. Es funktioniert genauso - das Argument dieses Operators wird ausgeführt, wenn der Bereich verlassen wird, in dem der Operator verwendet wird. Zusätzlich wird der Errdefer-Operator bereitgestellt, der ausgelöst wird, wenn ein Fehlertyp mit einem aktiven Fehlercode von der Funktion zurückgegeben wird. Dies ist Teil des ursprünglichen Zig-Fehlerbehandlungssystems.

Nicht erreichbarer Bediener


Das Element der Vertragsprogrammierung. Ein spezielles Schlüsselwort, das dort platziert wird, wo das Management unter keinen Umständen kommen sollte. Wenn es dort ankommt, wird in den Modi Debug und ReleaseSafe eine Panik erzeugt, und in ReleaseFast wirft das Optimierungsprogramm diese Zweige vollständig aus.

noreturn


Technisch gesehen ist es ein Typ, der in Ausdrücken mit jedem anderen Typ kompatibel ist. Dies ist möglich, weil ein Objekt dieses Typs niemals zurückkehren wird. Da Operatoren Ausdrücke in Zig sind, wird ein spezieller Typ für Ausdrücke benötigt, die niemals ausgewertet werden. Dies geschieht, wenn die rechte Seite des Ausdrucks die Kontrolle unwiderruflich an einen Ort außerhalb überträgt. Zu solchen Anweisungen brechen, fortfahren, zurückgeben, nicht erreichbare Endlosschleifen und Funktionen, die niemals die Kontrolle zurückgeben. Zum Vergleich: Ein Aufruf einer regulären Funktion (Rückgabe der Steuerung) ist kein Noreturn-Operator, da die Steuerung zwar nach außen übertragen wird, aber früher oder später an den Anrufpunkt zurückgegeben wird.

Somit werden folgende Ausdrücke möglich:

 fn foo(condition: bool, b: u32) void { const a = if (condition) b else return; @panic("do something with a"); } 

Die Variable a erhält den von der if / else-Anweisung zurückgegebenen Wert. Dazu müssen die Teile (sowohl if als auch else) einen Ausdruck des gleichen Typs zurückgeben. Der if-Teil gibt bool zurück, der else-Teil ist der noreturn-Typ, der technisch mit jedem Typ kompatibel ist. Daher wird der Code fehlerfrei kompiliert.

Funktionen


Die Syntax ist klassisch für Sprachen dieses Typs:

 fn add(a: i8, b: i8) i8 { return a + b; } 

Im Allgemeinen sehen die Funktionen ziemlich normal aus. Bisher habe ich keine Anzeichen für erstklassige Funktionen bemerkt, aber meine Kenntnis der Sprache ist sehr oberflächlich, ich könnte mich irren. Obwohl dies vielleicht noch nicht geschehen ist.

Ein weiteres interessantes Feature ist, dass in Zig das Ignorieren zurückgegebener Werte nur explizit mit dem Unterstrich _ erfolgen kann

  _ = foo(); 

Es gibt eine Reflexion, mit der Sie verschiedene Informationen über die Funktion erhalten können

 const assert = @import("std").debug.assert; test "fn reflection" { assert(@typeOf(assert).ReturnType == void); //    assert(@typeOf(assert).is_var_args == false); //    } 

Codeausführung zur Kompilierungszeit


Zig bietet eine leistungsstarke Funktion: Ausführen von Code, der zur Kompilierungszeit in Zick geschrieben wird. Damit der Code zur Kompilierungszeit ausgeführt werden kann, schließen Sie ihn einfach in einen Block mit dem Schlüsselwort comptime ein. Dieselbe Funktion kann sowohl zur Kompilierungszeit als auch zur Laufzeit aufgerufen werden, sodass Sie universellen Code schreiben können. Natürlich gibt es einige Einschränkungen, die mit verschiedenen Kontexten des Codes verbunden sind. In der Dokumentation in vielen Beispielen wird beispielsweise comptime verwendet, um die Kompilierungszeit zu überprüfen:

 // array literal const message = []u8{ 'h', 'e', 'l', 'l', 'o' }; // get the size of an array comptime { assert(message.len == 5); } 

Aber natürlich ist die Leistung dieses Betreibers hier bei weitem nicht vollständig offenbart. In der Beschreibung der Sprache wird daher ein klassisches Beispiel für die effektive Verwendung syntaktischer Makros gegeben - die Implementierung einer Funktion ähnlich wie printf, die jedoch die Formatzeichenfolge analysiert und alle erforderlichen Typprüfungen von Argumenten in der Kompilierungsphase durchführt.

Das Wort comptime wird auch verwendet, um die Parameter von Funktionen zur Kompilierungszeit anzugeben, die den C ++ - Vorlagenfunktionen ähnlich sind.

    fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } 

Fehlerbehandlung


Zig erfand ein ursprüngliches Fehlerbehandlungssystem, das anderen Sprachen nicht glich. Dies kann als "explizite Ausnahmen" bezeichnet werden (in dieser Sprache ist explizite Aussage im Allgemeinen eine der Redewendungen). Es sieht auch aus wie Go-Rückkehrcodes, funktioniert aber anders.

Das Zig-Fehlerverarbeitungssystem basiert auf speziellen Aufzählungen zur Implementierung eigener Fehlercodes (Fehler) und basiert auf deren Basis „Fehlertypen“ (algebraische Typensumme, die den zurückgegebenen Funktionstyp und den Fehlercode kombiniert).

Fehleraufzählungen werden wie reguläre Aufzählungen deklariert:

 const FileOpenError = error { AccessDenied, OutOfMemory, FileNotFound, }; const AllocationError = error { OutOfMemory, }; 

Alle Fehlercodes erhalten jedoch Werte größer als Null. Wenn Sie einen Code mit demselben Namen in zwei Aufzählungen deklarieren, erhält er denselben Wert. Implizite Konvertierungen zwischen verschiedenen Aufzählungen von Fehlern sind jedoch verboten.

Das Schlüsselwort anyerror bedeutet eine Aufzählung, die alle Fehlercodes enthält.

Wie optionale Typen unterstützt die Sprache die Generierung von Fehlertypen mithilfe einer speziellen Syntax. Typ! U64 ist eine abgekürzte Form von anyerror! U64, was wiederum eine Vereinigung (Option) bedeutet, die Typ u64 und type anyerror enthält (nach meinem Verständnis ist Code 0 reserviert, um das Fehlen eines Fehlers und die Gültigkeit des Datenfelds anzuzeigen, der Rest der Codes tatsächlich Fehlercodes).

Mit dem Schlüsselwort catch können Sie den Fehler abfangen und in einen Standardwert umwandeln:

 const number = parseU64(str, 10) catch 13; 

Wenn also ein Fehler in der Funktion parseU64 auftritt, der den Typ! U64 zurückgibt, wird catch ihn abfangen und den Standardwert 13 zurückgeben.

Mit dem Schlüsselwort try können Sie den Fehler an die obere Ebene (dh an die Ebene der aufrufenden Funktion) weiterleiten. Code anzeigen

 fn doAThing(str: []u8) !void { const number = try parseU64(str, 10); // ... } 

äquivalent dazu:

 fn doAThing(str: []u8) !void { const number = parseU64(str, 10) catch |err| return err; // ... } 

Hier passiert Folgendes: parseU64 wird aufgerufen, wenn ein Fehler von ihm zurückgegeben wird - es wird von der catch-Anweisung abgefangen, in der der Fehlercode mithilfe der in der err-Variablen platzierten Syntax "capture" extrahiert wird, die über! Void an die aufrufende Funktion zurückgegeben wird.

Der zuvor beschriebene Errdefer-Operator bezieht sich auch auf die Fehlerbehandlung. Der Errdefer-Argumentcode wird nur ausgeführt, wenn die Funktion einen Fehler zurückgibt.

Einige weitere Möglichkeiten. Mit dem || Sie können Fehlersätze zusammenführen

 const A = error{ NotDir, PathNotFound, }; const B = error{ OutOfMemory, PathNotFound, }; const C = A || B; 

Zig bietet auch Funktionen wie die Fehlerverfolgung. Dies ähnelt einer Stapelverfolgung, enthält jedoch detaillierte Informationen darüber, welcher Fehler aufgetreten ist und wie er sich entlang der Versuchskette vom Ort des Auftretens zur Hauptfunktion des Programms ausbreitet.

, Zig , C++, Go. , — 4 , ; — . ++, - . — .


Zig . , async, ( , ).

 test "create a coroutine and cancel it" { const p = try async<std.debug.global_allocator> simpleAsyncFn(); comptime assert(@typeOf(p) == promise->void); cancel p; assert(x == 2); } async<*std.mem.Allocator> fn simpleAsyncFn() void { x += 1; } 

async promise->T ( T — ). .

suspend, resume cancel. suspend . suspend, .

resume promise->T , .

cancel .

( ) . :

Bild

( ) — await. , , , (, ). , :

Bild


builtin functions — , . , « », . builtin' (sizeOf, tagName, TagType, typeInfo, typeName, typeOf), (import). builtin' C/C++ — , sqrt, popCount, slhExact .. , .

Abschließend


. , , . ++ , , , - . Rust , , . D — , , Java, - . Zig — . , .

Source: https://habr.com/ru/post/de435872/


All Articles