How to Actually Learn Zig (2027 Edition)
Cette vidéo présente Zig comme un langage système qui conserve la puissance de C tout en éliminant ses faiblesses, en démontrant la création d’un programme de tri pas à pas, avec une emphase sur l’explicite gestion de la mémoire et des erreurs.

SYNTHÈSE STRUCTURÉE
Introduction à Zig et son positionnement
Zig est présenté comme un langage de programmation système qui « ne veut pas abandonner la puissance que C offre tout en améliorant les défauts et faiblesses de C ». L’intervenant, Tony, le décrit comme « C mais en mieux, ou en d’autres termes, C++ plus ». Le langage rejette les concepts orientés objet jugés complexes, comme l’explique l’introduction humoristique : « Pour écrire une simple fonction calculant le volume d’une boîte, il faut d’abord inventer l’univers ». Cette philosophie se traduit par une approche où chaque opération est visible et contrôlable.
Configuration initiale et premier programme
Pour commencer, il faut créer un fichier sort.zig et utiliser la bibliothèque standard avec const std = @import("std"). La fonction principale se déclare avec pub fn main() void, et l’affichage se fait via std.debug.print. L’intervenant souligne une particularité : std.debug.print exige un second argument pour les paramètres de formatage, même vide, comme {}. Ce premier exemple montre la rigueur syntaxique de Zig, qui force à être explicite dès le départ.
Différence entre sortie standard et erreur standard
L’intervenant démontre une différence cruciale entre C et Zig : std.debug.print écrit sur stderr, pas sur stdout. En testant avec zig run sort.zig > zigout.txt, le fichier reste vide. Il explique : « print écrit sur l’erreur standard, pas la sortie standard. Mais plus important, il utilise un buffer de 64 octets pour l’impression formatée qui est vidé avant que la fonction ne retourne ». Cela en fait un outil de débogage, pas une solution pour la sortie standard.
Utilisation de l’API Writer pour la sortie standard
Pour écrire sur stdout, il faut utiliser l’API std.io.File.stdout(). L’intervenant montre comment passer std.process.init à main pour accéder à l’IO par défaut, puis créer un fichier, un buffer de 4096 octets, et un writer. La fonction writer() prend un fichier, un IO et un buffer, et retourne une structure avec print et flush. Il insiste : « N’oubliez pas de flush », car le buffer doit être vidé explicitement avec try output.flush().
Gestion explicite des erreurs avec try
Zig force la gestion des erreurs : « Vous êtes obligé de gérer chaque erreur ou de l’envoyer à l’appelant ». Le mot-clé try propage les erreurs, et le type de retour !void (ou bang void) indique que la fonction peut échouer. Cette approche élimine les erreurs silencieuses, un problème courant en C où les retours d’erreur sont souvent ignorés.
Lecture de l’entrée standard avec Reader
Pour lire, on utilise std.io.getStdIn().reader() avec un buffer dédié. La fonction readUntilDelimiterOrNull retourne un slice ou null en fin de flux, ce qui permet une boucle while (try input.readUntilDelimiterOrNull(line, '\n')) |line|. L’intervenant note que cette fonction retourne un type complexe : !?[]const u8, qu’il faut lire de droite à gauche : « un slice de u8 non signés, aka une chaîne, qui peut être null ou une erreur ».
Stockage des lignes avec ArrayList
Pour trier, il faut stocker les lignes dans un std.ArrayList. L’intervenant explique : « C’est une liste contiguë et extensible d’éléments en mémoire ». On crée une liste avec std.ArrayList([]const u8).empty, et on utilise append avec un allocateur. Zig exige de passer l’allocateur à chaque opération d’allocation : « Rien n’alloue sans que nous le voyions se produire ».
Gestion de la mémoire avec defer et dupe
Le mot-clé defer garantit la libération de la mémoire à la sortie du scope, quel que soit le chemin d’exécution. L’intervenant utilise defer lines.deinit(gpa) pour libérer la liste. Pour les lignes, il souligne un piège : readUntilDelimiterOrNull retourne un slice pointant vers le buffer, pas une copie. Il faut donc utiliser dupe pour copier chaque ligne : « Si nous voulons garder une ligne, nous devons la posséder ». Un bloc defer avec une boucle for libère chaque copie.
Tri avec std.mem.sort
Le tri utilise std.mem.sort([]const u8, lines.items, {}, lessThan). La fonction de comparaison lessThan prend un contexte (vide ici), et deux slices, retournant un booléen via std.mem.lessThan(u8, a, b). L’intervenant précise que le contexte sert pour des comparateurs avec état, comme trier par colonne. Le tri est effectué en une ligne après la boucle de lecture.
Comparaison avec C et C++
L’intervenant compare avec un programme C de 3 lignes utilisant printf et malloc implicites. Il explique : « La réponse est que nous n’avions pas besoin de tout ça. C’est une abstraction cachée dans libc ». Zig rend chaque allocation visible, ce qui réduit les bugs dormants : « C’est beaucoup plus difficile d’avoir des bugs latents si nous devons être explicites sur tout dès le départ ».
Conclusion et philosophie de Zig
Le programme final recrée sort en 20 minutes. L’intervenant résume : « Chaque allocation dans ce programme est quelque chose que nous avons demandé par son nom. Vous pouvez pointer vers chacune d’elles à l’écran ». Zig n’est pas une trahison de C, mais « C mais amélioré, pas une trahison de C, car C n’a jamais été cassé ». La philosophie est de rendre l’invisible visible.
CONCEPTS CLÉS
- Allocateur : En Zig, toute allocation mémoire doit être explicitement passée à la fonction qui en a besoin, contrairement à C où
mallocest implicite. - Slice : Une vue sur un tableau, composée d’un pointeur et d’une longueur, utilisée pour manipuler des chaînes et des buffers.
defer: Mot-clé qui exécute du code à la sortie du scope, garantissant la libération de ressources même en cas d’erreur.try: Opérateur qui propage les erreurs à l’appelant, forçant une gestion explicite.std.process.init: Structure fournissant l’IO standard et un allocateur généraliste, accessible viamain.
CONCLUSION
Le message principal est que Zig privilégie la clarté et le contrôle sur la concision. En rendant chaque allocation et chaque erreur visibles, il réduit les bugs latents et facilite la maintenance à long terme. Pour un administrateur système, cela signifie des programmes plus prévisibles et plus faciles à auditer, au prix d’une verbosité assumée. Comme le dit l’intervenant : « Ce n’est pas que Zig fait moins, c’est que Zig le fait là où vous pouvez le voir ».