De Zig et des zags

Posté par  . ÉditĂ© par _seb_, orfenor, Ysabeau đŸ§¶ 🧩, BAud, palm123, BenoĂźt Sibaud, Strash et Arkem. ModĂ©rĂ© par patrick_g. Licence CC By‑SA.
Étiquettes :
50
7
sept.
2023
Programmation

Cette dĂ©pĂȘche, sous son titre Ă©nigmatique, va vous prĂ©senter un langage de programmation relativement nouveau, nommĂ© Zig, avec ses caractĂ©ristiques principales ainsi que son Ă©cosystĂšme (toolchain). Le but n’est pas de vous faire Ă  tout prix adopter ce langage, mais seulement de dĂ©couvrir quelques morceaux choisis, pour le plaisir des yeux.
Logo de Zig

Sommaire

Note : ce langage est relativement jeune, la premiĂšre version date de 2016 et la derniĂšre version stable (0.10.1) parue en janvier 2023 est dĂ©jĂ  considĂ©rĂ©e comme obsolĂšte par rapport Ă  la branche master ! Le dĂ©veloppement est donc trĂšs actif et les diverses documentations ne peuvent rester Ă  jour bien longtemps. Une nouvelle version 0.11.0 est d’ailleurs sortie pendant la rĂ©daction de la dĂ©pĂȘche.

Présentation

Le langage Zig est une idĂ©e d’Andrew Kelley. Il est l’un des contributeurs principaux et travaille Ă  plein temps sur le projet.

Zig est un langage compilé impératif polyvalent, typé statiquement, « bas niveau » type C/C++/Rust. Il se décrit ainsi :

Zig est un langage de programmation gĂ©nĂ©raliste ainsi qu’une toolchain ayant pour objectifs la robustesse, l’optimisation et la rĂ©utilisation du code.

Ce langage se veut plus simple que ses prédécesseurs (« Concentrez-vous sur le debug de votre application et non sur le debug de votre connaissance du langage. ») et néanmoins puissant et sécurisé.

Il a aussi l’ambition avouĂ©e dĂšs ses dĂ©buts de vouloir ĂȘtre un C meilleur que C, une vĂ©ritable Ă©volution, tout en restant compatible. C’est pourquoi vous pouvez faire en Zig tout ce que vous faisiez en C, mais en mieux.

Installation

L’installation de Zig est des plus simples et universelle, puisqu’il s’agit d’une simple archive tar Ă  extraire. On y trouve un exĂ©cutable nommĂ© zig qui contient tous les outils nĂ©cessaires, et quelques fichiers de documentation ainsi que la librairie standard zig. (À noter que la source de la libc musl est Ă©galement fournie pour pouvoir compiler un bon nombre de cibles, bien que Zig n’en dĂ©pende pas.) Le tout tient dans une petite archive de 45 Mo, ce qui est fort raisonnable.

La deuxiĂšme Ă©tape, optionnelle, consiste Ă  permettre l’appel Ă  l’exĂ©cutable zig depuis n’importe oĂč, soit en ajoutant le dossier contenant zig au PATH, soit en crĂ©ant un lien symbolique pointant sur cet exĂ©cutable dans votre dossier bin.

Notons que certains gestionnaires de paquets proposent dĂ©jĂ  un package zig, mĂȘme si parfois celui-ci est obsolĂšte Ă  cause du dĂ©veloppement trĂšs actif de Zig.

Hello, world!

Voici le traditionnel « Bonjour, le monde ! » :

const std = @import("std");

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

Dans votre terminal :

$ zig build-exe hello.zig

$ ./hello
Hello, world!

Un autre exemple qui sera peut-ĂȘtre plus intĂ©ressant, il s’agit d’extraire des entiers d’une chaine de caractĂšre, sĂ©parĂ©s par des espaces ou des virgules. À noter que ce programme s’auto-teste :

const std = @import("std");
const parseInt = std.fmt.parseInt;

test "parse integers" {
    const input = "123 67 89,99";
    const ally = std.testing.allocator;

    var list = std.ArrayList(u32).init(ally);
    // Ensure the list is freed at scope exit.
    // Try commenting out this line!
    defer list.deinit();

    var it = std.mem.tokenize(u8, input, " ,");
    while (it.next()) |num| {
        const n = try parseInt(u32, num, 10);
        try list.append(n);
    }

    const expected = [_]u32{ 123, 67, 89, 99 };

    for (expected, list.items) |exp, actual| {
        try std.testing.expectEqual(exp, actual);
    }
}

D’autres exemples sont disponibles sur https://ziglearn.org/.

Vous pouvez aussi voir le code d’un TapTempo abondamment commentĂ©.

Un mot sur l’écosystĂšme

Zig fournit une chaĂźne de compilation qui compile le langage Zig, mais aussi C et C++. Cette chaĂźne de compilation prend en charge la cross-compilation ainsi que la compilation statique. Zig intĂšgre Ă©galement son propre systĂšme de construction rendant superflus l’usage de Make/Cmake/etc. Un gestionnaire de paquets est prĂ©vu, pour gĂ©rer aussi bien les bibliothĂšques Ă©crites en C qu’en Zig. La version 0.11.0 a commencĂ© Ă  introduire ce gestionnaire de paquets et il est dĂ©jĂ  considĂ©rĂ© suffisamment stable pour ĂȘtre utilisĂ©.

Le compilateur est Ă  lui seul une petite merveille puisqu’il est fourni avec musl (une implĂ©mentation de la bibliothĂšque standard C) qu’il compile Ă  la demande et utilise pour gĂ©nĂ©rer des binaires statiques. Il peut construire des exĂ©cutables pour un grand nombre d’OS et d’architectures, intĂšgre un cache de compilation et quatre modes de compilation. Il supporte mĂȘme une gĂ©nĂ©ration du code en WebAssembly sans manipulations Ă©sotĂ©riques, simplement en spĂ©cifiant la cible appropriĂ©e.

Il est Ă  noter qu’en mode de compilation Release, le compilateur est dĂ©terministe jusque dans ses optimisations, et il produira le mĂȘme binaire pour la mĂȘme source, au bit prĂšs !

Tout ceci fait que le compilateur C du projet Zig est tout à fait honorable (voir plus pour certains) face aux principaux compilateurs C du marché.

Spécificité du langage

Quant au langage lui-mĂȘme, il est conçu pour ĂȘtre simple et lisible, sans rien de cachĂ© : ni flots de contrĂŽle, ni allocations de mĂ©moire.
Si un code en Zig ne semble pas faire appel Ă  une fonction, c’est qu’il ne le fait pas. L’objectif de cette conception est d’amĂ©liorer la lisibilitĂ©.

Zig ne gĂšre pas lui-mĂȘme les allocations mĂ©moire sur le tas. Il n’y a pas de mot clĂ© new ou autre fonctionnalitĂ© qui utiliserait un allocateur de mĂ©moire (comme un opĂ©rateur de concatĂ©nation de chaĂźnes de caractĂšres par exemple). Le concept de tas est gĂ©rĂ© par une bibliothĂšque ou le code de l’application, pas par le langage. Le principal problĂšme avec les allocations de mĂ©moire cachĂ©es est qu’elles empĂȘchent la rĂ©utilisation du code dans certains environnements. Certains cas nĂ©cessitent de n’avoir aucune allocation mĂ©moire, donc le langage de programmation doit fournir cette garantie.

La bibliothĂšque standard de Zig est entiĂšrement optionnelle. Chaque API n’est compilĂ©e dans le programme que si elle est utilisĂ©e. Zig a la mĂȘme prise en charge avec ou sans libc. Zig encourage son utilisation directement sur le matĂ©riel et le dĂ©veloppement Ă  haute performance. Ceci est le meilleur des deux mondes. Par exemple, les programmes WebAssembly peuvent utiliser les fonctionnalitĂ©s habituelles de la bibliothĂšque standard, et quand mĂȘme avoir des exĂ©cutables de petite taille comparĂ©s aux autres langages prenant en charge WebAssembly.

Zig n’a pas de macros ni de mĂ©taprogrammation, et pourtant le langage exprime des programmes complexes d’une maniĂšre claire, non rĂ©pĂ©titive. MĂȘme Rust implĂ©mente en dur certaines macros, comme format!. L’équivalent en Zig est implĂ©mentĂ© dans la bibliothĂšque standard sans code en dur dans le compilateur.

Zig 0.5.0 a introduit les fonctions async. Cette fonctionnalitĂ© n’a pas de dĂ©pendance au systĂšme d’exploitation hĂŽte ou mĂȘme Ă  l’allocation de mĂ©moire dans le tas. Cela veut dire que les fonctions async sont disponibles pour la cible « freestanding » (sans systĂšme d’exploitation).

Dans d’autres langages de programmation, les rĂ©fĂ©rences null sont sources d’erreurs Ă  l’exĂ©cution, et sont mĂȘme soupçonnĂ©es ĂȘtre la pire erreur en informatique. Les pointeurs en Zig ne peuvent pas ĂȘtre null. Il existe nĂ©anmoins un type optionnel.

Zig dĂ©fini un type spĂ©cial de gestion des erreurs. Les erreurs ne peuvent ĂȘtre ignorĂ©es. Zig vous permet d’énumĂ©rer toutes les raisons possibles de dĂ©faillance de maniĂšre exhaustive et de traiter chacune d’entre elles de maniĂšre spĂ©cifique si vous le souhaitez. Cette Ă©numĂ©ration est produite et vĂ©rifiĂ©e par le compilateur (ce qui fait partie des particularitĂ©s du langage Zig), de sorte que vous ne pouvez manquer aucun cas. Il convient donc d’en tenir compte et de les traiter au moyen des mots-clĂ©s catch, try, switch et unreachable.

Il a Ă©tĂ© dit plus haut que Zig n’a pas de macros. Mais il a mieux. Il peut exĂ©cuter du code Ă  la compilation plutĂŽt qu’à l’exĂ©cution. En effet, une variable marquĂ©e du mot-clĂ© comptime sera connue Ă  la compilation. Les boucles marquĂ©es du mot-clĂ© inline seront exĂ©cutĂ©es Ă  la compilation. Cela permet d’exĂ©cuter Ă  l’avance les parties du code qui ne dĂ©pendent pas des entrĂ©es utilisateur, et d’optimiser l’exĂ©cution, ou simplement automatiser des tĂąches rĂ©pĂ©titives pour le programmeur sans que cela ne se ressente Ă  l’exĂ©cution.
Un exemple : je veux que mon programme affiche son nom Ă  diffĂ©rent stade de son exĂ©cution, avec de grandes lettres en art ASCII. On dispose pour cela d’une chaĂźne de caractĂšre contenant le nom du programme, et d’une fonction foo qui retourne une chaine avec de grandes lettres en art ASCII en Ă©change d’une chaĂźne de caractĂšre. La plupart du temps, on se contenterait d’appeler une fois la fonction foo au dĂ©but du programme et Ă  stocker dans une variable le rĂ©sultat pour l’afficher.
Mais Zig permet d’aller plus loin. Avec l’exĂ©cution Ă  la compilation, on peut n’exĂ©cuter la fonction donnant les grandes lettres qu’à la compilation, augmentant ainsi lĂ©gĂšrement le temps d’exĂ©cution. La fonction foo n’étant pas utile Ă  l’exĂ©cution, elle ne sera pas incluse dans le binaire, tout comme la chaĂźne contenant le nom du programme.
Cet exemple est trivial, mais peut vous donner un aperçu de la puissance du comptime.

Pour un article plus détaillé sur le comptime Zig, voir https://zig.news/edyu/wtf-is-zig-comptime-and-inline-257b

Vous pourrez en apprendre encore plus en lisant la vue d’ensemble du projet, ou en jouant avec les ziglings !

Les outils

Il n’existe Ă  ce jour aucun IDE conçu pour Zig, mais certains Ă©diteurs de texte supportent dĂ©jĂ  des fonctionnalitĂ©s telles que la coloration syntaxique. Ces Ă©diteurs sont Emacs, Vim, Kate, VS Code et Sublime Text.
Un serveur de langue est fourni Ă©galement pour les clients le supportant.

Quant au dĂ©bogage, il peut se faire tout simplement avec gdb, ou n’importe quel dĂ©bogueur C/C++. NĂ©anmoins, le compilateur fournit des indications si prĂ©cises sur les erreurs qu’on a pu commettre que le recours Ă  un dĂ©bogueur pour la plupart des erreurs triviales est inutile.

Les librairies Zig, elles, sont, pour l’instant, peu nombreuses, et il n’existe pas d’index « officiel » pour les rĂ©fĂ©rencer puisque la communautĂ© est dĂ©centralisĂ©e. À la place, on retrouve plusieurs petits index maintenus par la communautĂ©.
Il existe tout de mĂȘme un projet en cours de gestionnaire de paquet livrĂ© avec Zig, qui n’est reliĂ© Ă  aucun index en particulier, et qui gĂšrera aussi bien les modules Zig que C/C++.

Usage

Zig est un langage de programmation assez généraliste, il peut donc convenir à la plupart des usages.

Il est notamment plĂ©biscitĂ© pour des programmes ayant des contraintes de performance et de consommation mĂ©moire que l’on peut retrouver dans les systĂšmes embarquĂ©s et applications en « temps rĂ©el ». Une communautĂ© d’utilisateurs (des ziguanas) dans ce domaine (zig embedded group) s’organise autour du projet microzig afin d’offrir l’outillage nĂ©cessaire pour ce type de dĂ©veloppement.

On peut trouver plusieurs listes de projets Ă©crits en Zig, parmi lesquels on trouve un lecteur de flux Fediverse pour bureau GTK, un solveur de sudoku, un interprĂ©teur Brainfuck, un Ă©diteur de pixel art, un systĂšme d’exploitation, un environnement d’exĂ©cution JavaScript, un jeu RPG 2D
 Les usages de Zig sont multiples et variĂ©s !

Zig est aussi utilisé par des entreprises comme Uber ou TigerBeetle.

La version 0.11.0

Cette sortie de version inclut 8 mois de travail : des changements de la part de 269 contributeurs diffĂ©rents, rĂ©partis dans 4457 changements. Un total de 1012 tickets rĂ©solus ! C’est Ă©galement « the dĂ©but of Package Management ».

Le mot de la fin

Zig est sous licence MIT. L’organisation Zig Software Foundation est une organisation Ă  but non lucratif 501(c)(3)) dont l’objectif est de soutenir le projet Zig que ce soit par la rĂ©munĂ©ration, voire, l’embauche, de dĂ©veloppeurs (Jakub Konka, Rich Felker, Meghan Denny, Andrew Kelley) ou le sponsoring d’autres projets gravitant autour du projet (Zig books par exemple ou indirectement musl).

Zero the Ziguana, une des mascottes de Zig

Aller plus loin

  • # Zig c'est la vie !

    Posté par  . Évalué à 6.

    Merci pour cette dĂ©pĂȘche !

    Plus je lis sur Zig et plus j'aime ce langage.
    Je ne l'utiliserai certainement pas pour tous les usages, mais quand on fait de l'embarqué ou du systÚme, c'est vraiment chouette.

    Le systĂšme de build intĂ©grĂ© au langage est au dĂ©but assez dĂ©routant, mais l'idĂ©e de pouvoir tout faire dans le mĂȘme langage et avec un seul binaire est vraiment sĂ©duisante.

    J'apprécie aussi particuliÚrement la possibilité de pouvoir utiliser une bibliothÚque C par un simple @cImport
    J'adore ce petit exemple, qui montre que Zig est plus lisible que C mĂȘme en appelant des bibliothĂšques C. Bref, c'est un meilleur C, comme le dit son auteur.

    Le comptime aussi c'est juste magique, je n'ai pas encore trop jouĂ© avec, mais ça donne plein d'idĂ©es. J'avais Ă©tĂ© impressionnĂ© par le code de print prĂ©sentĂ© dans la doc. Ça vaut le coup de passer un peu de temps pour comprendre une telle fonction.
    Ce qui est fou c'est que comptime permet de faire de la généricité sans rien ajouter au langage !

    Il y a plein d'autres aspects à explorer comme la gestion "sécurisée" des pointeurs.

    Il y a aussi plein de conf intĂ©ressantes Ă  voir sur le sujet, notamment celle sur le recodage de la libC, malheureusement je n'arrive pas remettre la main dessus, peut-ĂȘtre que c'Ă©tait pas ça le titre


    Bref, c'est un beau projet qui a de beaux jours devant lui.

  • # Mais pourquoi diantre pas de RAII ?

    Posté par  (site web personnel) . Évalué à 10.

    Merci beaucoup pour cette dĂ©pĂȘche trĂšs sympa ;-)

    Je trouve globalement Zig trÚs rafraßchissant et bourré de bonnes idées.

    Typiquement l'intĂ©gration d'un compilo C capable de cross-compilation dans quelques dizaines de Mo. Ça donne mĂȘme des idĂ©es pour les logiciels ayant besoin d'embarquer un compilo pour faire du JIT.

    Bref j'en arrive à ma question: mais pourquoi donc des gens aussi compétents refusent de mettre du RAII (c'est à dire des fonctions destructeur appelées automatiquement quand un objet se fait free ou bien est enlevé de la stack) ?

    De ce que je comprends l'argument avancé c'est:
    1 - c'est pour qu'il n'y ait rien de caché
    2 - c'est plus lisible car c'est explicite

    Pour le premier point on peut objecter que nos CPU modernes ne se privent pas pour faire toute sorte de magie noire avec notre code (out-of-order, processeur super-scalaires).
    L'OS de sont cĂŽtĂ© dĂ©cide arbitrairement du scheduling des threads, dĂ©clenches des interruptions quand ça lui chante et fait semblant d’allouer de la mĂ©moire quand on fait un malloc pour finalement tuer notre process sur un out-of-memory quand il a Ă©tĂ© trop optimiste

    Bref des choses cachées quand on un impact sur notre programme c'est pas ça qui manque dans un ordinateur moderne !

    Le second point me semble encore plus incompréhensible: on se retrouve avec un pattern consistant à systématiquement devoir écrire var foo = init(); defer foo.destroy();.
    - Si on oublie le defer on a fait un bug
    - Donc faut faire gaffe à ça en code review
    - Et faut des outils pour détecter ce type d'erreurs
    - Et ça peut ĂȘtre plein de classes de bug: memory leak, resource leak (e.g. on ne ferme pas un fichier), deadlock (e.g. la ressource reprĂ©sente la prise de mutex)

    En rÚgle générale avoir un langage qui permet d'exprimer des structures ne pouvant pas représenter un état invalide est un gain énorme (exemple typique: en Rust un mutex contient l'objet qu'il protÚge, de fait il est impossible d'accéder à ce dernier sans avoir pris le mutex).

    Et si on veut pinailler, defer est dĂ©jĂ  quelque de magique vu qu'on n'est pas explicite sur l'endroit oĂč sera fait l'appel de fonction (j'imagine que personne n'est assez fou pour dĂ©fendre l'utilisation de free sans defer ni RAII comme en C ).
    De fait plutÎt qu'un defer, j'aurai plutÎt vu un mot clé permettant d'indiquer qu'une fonction retournait un object avec destructor.
    Quitte Ă  devoir explicitement fournir le destructeur dans la fonction d'initialisation si on veut rester plus explicite, par exemple:

    var foo = raii foo_factory() -> foo_destructor; // pseudo syntax,
    On peut mĂȘme obliger les variables raii Ă  avoir un nom particulier (genre avec un symbole en prĂ©fixe) Ă©tant donnĂ© que la variable peut survivre Ă  la fonction l'ayant crĂ©Ă©e (typiquement en la retournant Ă  l'appelant), ce qui Ă©tait moins utile pour le defer car il s'exĂ©cute uniquement dans la fonction d'oĂč il est dĂ©clarĂ©.

    Bref, de ma fenĂȘtre le RAII apporte des avantages monstrueux comparĂ©s Ă  ses "inconvĂ©nients" thĂ©orique (et ça semble plutĂŽt simple de faire un defer sous stĂ©roĂŻde qui garde le meilleurs des deux mondes). Et du coup je n'arrive Ă  comprendre le raisonnement des devs de Zig (qui, je le rappelle sont des mecs sacrĂ©ment brillants !)

    Dernier point: on n'est pas vendredi, ce post n'est pas un troll (ni un flim sur le cyclimse). Il est bien possible que j'ai fait un homme de paille sans le vouloir avec les arguments pour l'absence de RAII, si c'est le cas merci de me corriger dans la joie et la bonne humeur ;-)

    • [^] # Re: Mais pourquoi diantre pas de RAII ?

      Posté par  . Évalué à 1.

      En mĂȘme temps, vu que Zig peut utiliser un "module" C++ qui lui supporte le RAII, rien ne t'empĂȘche de RAIIser ton code en C++ et de l'utiliser dans Zig, non ?

    • [^] # Re: Mais pourquoi diantre pas de RAII ?

      Posté par  . Évalué à 1.

      Le RAII, c'est surtout de la performance processeur en moins (car il faut surveiller l'état des variables à l'exécution) et en embarqué c'est aussi une consommation mémoire non maßtrisé (on ne sait pas quand il va passer libérer la mémoire).

      Le RAII simplifie grandement la programmation mais quand on recherche des performances optimale, c'est du gùchis. Enfin la solution C++ est pas mal avec les smartpointer que l'on peut utiliser quand on accepte cette "perte" pour plus de productivité.

      • [^] # Re: Mais pourquoi diantre pas de RAII ?

        Posté par  (site web personnel) . Évalué à 7.

        Le RAII, c'est surtout de la performance processeur en moins [
] c'est aussi une consommation mémoire non maßtrisé

        Je pense que tu confonds RAII et garbage collector ;-)

        RAII n'a strictement aucun impacte au runtime: c'est le compilo qui dĂ©termine oĂč une variable n'est plus utilisĂ©e et met Ă  cet endroit le code d'appel du destructeur.

        Si tu ne fais pas de RAII tu devras mettre Ă  la main l'appel au destructeur au mĂȘme endroit (dans tous les cas faut bien faire un close sur le file descriptor ou un free sur le buffer !).

        (et les smartpointers de C++ sont implémentés grùce au RAII)

        • [^] # Re: Mais pourquoi diantre pas de RAII ?

          Posté par  . Évalué à 2.

          Le compilo ne peut pas toujours savoir quand mettre un appel au destructeur. Les smartpointer, c'est en quelques sortes un garbage collector compilĂ© avec le code (Enfin, c'est Go qui fonctionne rĂ©ellement comme ça). Mais au lieu d'avoir un processus sĂ©parĂ© qui regarde le nombre de pointeurs sur chaque instance le smartpointer vĂ©rifie s'il est Ă  0 et dĂ©truit l'instance si besoin (le travail du GC). Certes, l'implĂ©mentation est sans doute un peu plus complexe/optimisĂ© avec des destruction "prĂ©visibles" mais c'est le principe. Il y a donc du code qui "compte et regarde", c'est de la ressources perdu. Certes c'est peanuts, mais des variables, il peut y en avoir beaucoup, sur des langages ou tout est gĂ©rĂ© ainsi, l'incidence est loin d'ĂȘtre nĂ©gligeable.

          • [^] # Re: Mais pourquoi diantre pas de RAII ?

            Posté par  (site web personnel) . Évalué à 8.

            Mais là tu parles de garbage collector (dont une des implémentation est le compteur de références qui est mis en oeuvre dans les smartpointers C++)

            Le garbage collector est utilisé uniquement pour les allocations dynamiques (si une allocation a lieu sur la stack, on sait que sa durée de vie est liée à l'appel de fonction l'ayant créé, donc pas besoin de check à la runtime pour savoir quand la libérer).

            Le RAII est un concept orthogonal au garbage collector: si ton smartpointer est capable de dĂ©crĂ©menter le compteur de rĂ©fĂ©rence et de faire la libĂ©ration de mĂ©moire quand celui-ci arrive Ă  zĂ©ro, c'est bien que du code a Ă©tĂ© appelĂ© automatiquement quand la variable contenant le smarpointer a Ă©tĂ© dĂ©truite. Et cela quelque soit l'endroit oĂč se trouve la variable ne question (stack, dans une structure elle-mĂȘme sur le tas etc.).
            De plus le RAII peut servir a gérer autre chose que de la mémoire: fichier/socket ouvert, mutex etc.

            (et Go n'utilise pas de compteur de référence pour son garbage collector, mais du mark and sweep)

    • [^] # Re: Mais pourquoi diantre pas de RAII ?

      Posté par  . Évalué à 2.

      Zig a déjà des mécanismes permettant de détecter les fuites de mémoire (sauf en ReleaseFast), mais c'est vrai que le principe de RAII est intéressant.

      ça s'implémente bien en C++ car il y a cette notion de destructeur. Dans un langage comme Zig, il faudrait trouver un moyen d'associer une fonction à une structure de donnée qu'on alloue.

      AprĂšs, comme tu le dis, ça pourrait passĂ© par une sorte de "super defer" ou peut-ĂȘtre justement par les allocateurs ?
      Comme on passe l'allocateur, on pourrait peut-ĂȘtre prĂ©ciser si la mĂ©moire doit ĂȘtre libĂ©rĂ©e Ă  la sortie du scope ? Bon dans tous les cas, tu pourrai oubliĂ© d'utiliser cet allocateur.

      De ce que j'avais compris des premiĂšres prĂ©sentation de Zig, Andrew semblait avoir des idĂ©es pour amĂ©liorer la gestion de la mĂ©moire. Le langage est encore jeune, peut-ĂȘtre que ce genre de changement est encore possible ? Ça a peut-ĂȘtre Ă©tĂ© proposĂ© (j'ai pas regardĂ©) ?

    • [^] # Re: Mais pourquoi diantre pas de RAII ?

      Posté par  (site web personnel) . Évalué à 6.

      J'en rajoute une couche avec ce post de l'auteur de Zig

      RAII works fine in Zig

      Il semble considérer que le defer fait office de RAII. Mais c'est oublier que le defer s'exécute quand on quitte la fonction dans laquelle il a été déclaré. Du coup on ne peut pas retourner des objets avec destructeur.

      Par exemple:

      const std = @import("std"); 
      
      const LockGuard = struct {
          mutex: *std.Thread.Mutex,
      
          fn take(mutex: *std.Thread.Mutex) !LockGuard {
              std.debug.print("Lock!\n", .{});
              mutex.lock();
              return .{
                  .mutex = mutex,
              };
          }
      
          fn release(lg: *LockGuard) void {
              std.debug.print("Unlock!\n", .{});
              lg.mutex.unlock();
          }
      };
      
      var m: std.Thread.Mutex = .{};
      fn take_lock() !LockGuard {
          var lk = try LockGuard.take(&m);
          defer lk.release();
      
          return lk;
      }
      
      pub fn main() !void {
          std.debug.print("taking lock!\n", .{});
          var guard = try take_lock();
          std.debug.print("lock taken {}!\n", .{guard});
      }
      

      va retourner (vous pouvez aller sur https://zig-play.dev/ pour jouer avec):

      taking lock!
      Lock!
      Unlock!
      lock taken [...] !

      alors qu'un langage supportant le RAII n'aurait libéré le lock que quand guard n'est effectivement plus référencé, c'est à dire à la fin de la fonction main !

      Et puisque maintenant c'est Vendredi, je vous laisse avec la justification pour fermer l'issue pour faire du vrai RAII en Zig ;-)

      • [^] # Re: Mais pourquoi diantre pas de RAII ?

        Posté par  . Évalué à 2.

        alors qu'un langage supportant le RAII n'aurait libéré le lock que quand guard n'est effectivement plus référencé, c'est à dire à la fin de la fonction main !

        Hum, oui mais en Zig tu ne l'aurai pas codé comme ça. Vu que Zig n'a pas de notion de destructeur, t'es obligé de mettre ton defer (et le unlock) dans le main (c'est d'ailleurs ce qu'il fait dans le post reddit que tu as cité).

        Et puisque maintenant c'est Vendredi, je vous laisse avec la justification pour fermer l'issue pour faire du vrai RAII en Zig ;-)

        La proposition du mot clé clean est intéressante, aprÚs je ne sais pas si ça rentre dans la philosophie du langage car ça masque le fait que ça va appeler une fonction, puis tu pourrai toujours oublié d'appeler clean.
        Mais c'est moins verbeux que ce qui est pratiqué aujourd'hui.

        Je comprends la volontĂ© du/des crĂ©ateurs de Zig d'ĂȘtre trĂšs conservateurs sur ce qui rentre ou pas dans le langage. AprĂšs, je trouve que cette gestion de la mĂ©moire avec allocateur explicite est trop lourde dans bien des cas, c'est pourquoi je rĂ©serverai Zig pour de l'embarquĂ©, de la programmation systĂšme ou des librairies qui ont besoin d'ĂȘtre trĂšs optimisĂ©es (genre librairie standard d'un langage, ce que fait Bun ou Roc).

        • [^] # Re: Mais pourquoi diantre pas de RAII ?

          Posté par  (site web personnel) . Évalué à 4. DerniĂšre modification le 13 septembre 2023 Ă  00:08.

          Vu que Zig n'a pas de notion de destructeur, t'es obligé de mettre ton defer (et le unlock) dans le main

          Oui tout Ă  fait, mais du coup on se retrouve avec une fonction qui retourne une ressource devant ĂȘtre impĂ©rativement nettoyĂ©e
 mais rien pour garantir que ce sera fait ! (le fameux RTFM quoi ÂŻ\(ツ)/ÂŻ )

          C'est le retour du bug classique façon:

          int foo() {
            lock_mutex();
            if (a) {
              // fait plein de truc compliqués
              release_mutex();
              return x;
            } else if (b) {
              // encore d'autres trucs compliqués
              release_mutex();
              return y;
            } else if (c) {     // Hop ! une PR ajoute cette nouvelle condition
              // toujours plus de trucs compliqués...
              // ... et on a oublié de release le mutex, c'est vendredi la review est un peu molle, ça part en prod, Ariane 5 explose en vol
              return z;
            }
          }

          La solution qu'a choisi Zig pour contrer ce problÚme est un puissant systÚme de vérification à la runtime quand on compile avec le profile de test (et aussi en mode release-safe).
          C'est largement mieux que C, mais ça ne vaut pas une vérification exhaustive à la compilation (comme permet le RAII): on ne détectera le problÚme que si on passe dedans pendant notre phase de test.
          Et donc c'est typiquement le code peu utilisé (ou galÚre à tester) genre gestion de cas d'erreur dans un cas aux limites qui se retrouve à contenir des bugs (comprendre: les bugs les plus horribles à reproduire)

          Enfin, autant ce type de checks marche pour vérifier des erreurs d'allocations genre use-after-free, autant il n'est d'aucune utilité pour de la gestion de ressource comme dans mon exemple


          c'est pourquoi je rĂ©serverai Zig pour de l'embarquĂ©, de la programmation systĂšme ou des librairies qui ont besoin d'ĂȘtre trĂšs optimisĂ©es

          J'ai l'impression que Zig est trĂšs sĂ©duisant pour les devs C car on garde la philosophie une trĂšs forte compatibilitĂ© avec le C (Zig se vent comme Ă©tant capable de s'intĂ©grer dans un projet C existant) tout en fournissant un langage avec tooling et Ă©cosystĂšme au bas mot 20ans en avance par rapport au C99
 Ho wait !

    • [^] # Re: Mais pourquoi diantre pas de RAII ?

      Posté par  . Évalué à 3.

      Vale un language en alpha a mĂȘme un RAII ou les destructeurs peuvent prendre des paramĂštres:
      https://verdagon.dev/blog/higher-raii-7drl

  • # Que du bon

    Posté par  . Évalué à 6.

    Je ne me suis pas lancé dans Zig car cela reste pour moi un langage compliqué, comme Rust, et dont je n'ai pas usage immédiat. Je ne fais pas de programmation systÚme. Je suis plus sur du Python/Nim/Elixir.

    Mas j'adore les contenus à propos de zig, vidéos et billets de blog. Je trouve que c'est plein de bonnes idées, que le créateur a semble visionnaire et trÚs sympathique, la communauté trÚs animée.

    Il commence à y avoir plein de bonnes réalisations. En vrac: une réécriture de ncdu, écrire des traitements rapides pour Elixir (en fait pour la BEAM), un serveur web.

    À noter, Uber utilise Zig comme beaucoup de monde : pour sa toolchain qui permet de (cross-)compiler du C.

    Je l'ai utilisé pour cross-compiler du Nim de amd64 vers arm7, c'est terriblement efficace, par rapport à gcc.

    Mais le projet se questionne sur ce qui semble une bonne idĂ©e pour crĂ©er de l'adoption mais pourrait les freiner dans le dĂ©veloppement d'une toolchain plus efficace pour le langage lui-mĂȘme.

    Un langage réussi c'est un mélange de:
    - ses fonctionnalités, sa syntaxe : comment ça m'aide à produire du code qui me plait
    - son tooling : comment je passe du code Ă  qqch qui tourne
    - son Ă©cosystĂšme, bibliothĂšque, framework : comment je ne pars pas de rien
    - sa communauté : comment j'obtiens de l'aide, des idées

    Je pense que Zig est bien parti sur tous ces aspects.

    • [^] # Re: Que du bon

      Posté par  . Évalué à 2.

      Mais le projet se questionne sur ce qui semble une bonne idĂ©e pour crĂ©er de l'adoption mais pourrait les freiner dans le dĂ©veloppement d'une toolchain plus efficace pour le langage lui-mĂȘme.

      Je ne savais pas qu'ils souhaitaient Ă  long terme se dĂ©faire de LLVM. C'est un noble objectif et en mĂȘme temps super ambitieux !
      Ils ont l'air d'ĂȘtre dĂ©terminĂ© et j'ai hĂąte de voir ce que ça peut donner.

  • # Les pointeurs peuvent ĂȘtre null

    Posté par  . Évalué à 1.

    Contrairement à ce qui est indiqué dans l'article, on peut parfaitement avoir des pointeurs null dans Zig. Cf cet exemple. Sinon, impossible d'implémenter une liste chaßnée, une queue, etc


  • # bien mais pas top

    Posté par  . Évalué à 0. DerniĂšre modification le 08 septembre 2023 Ă  10:19.

    Pour moi Rust est juste mieux quand on veut de la performance, il permet de tout optimiser:
    - allocation mémoire sûr (plus que Zig)
    - macro qui permettent de dĂ©porter Ă  la compilation tout ce qui peut l'ĂȘtre (mĂȘme plus que C)

    Alors certes Rust est plus complexe (quoique) mais si on veut des performances au top
 sinon on prends Go ou Java.

    Zig n'est pas inintéressant pour autant.

    • [^] # Re: bien mais pas top

      Posté par  . Évalué à 6.

      Pour moi Rust est juste mieux quand on veut de la performance, il permet de tout optimiser:

      Je ne comprend en quoi Rust est plus performant concernant l'allocation mémoire ?
      Ni en quoi les macros de Rust sont plus efficaces que le comptime de Zig.

      Pour moi, l'intĂ©rĂȘt principal de Rust reste la garantie qu'il n'y aura pas de problĂšme avec la mĂ©moire (fuite etc.), mais niveau perf, ce sont deux langages qui permettent d'avoir des performances similaires.

      Peut-ĂȘtre mĂȘme que Zig encourage des façon de coder plus adaptĂ©s Ă  la performance, comme le "Data Oriented Design", notamment avec des sucres syntaxiques pour pouvoir facilement boucler sur des structures de tableaux.

      En tout cas, Zig est rĂ©putĂ© pour permettre d'Ă©crire du code extrĂȘmement performant.

      • [^] # Re: bien mais pas top

        Posté par  . Évalué à 2.

        mais niveau perf, ce sont deux langages qui permettent d'avoir des performances similaires.

        Et encore, en Rust, ponctuellement, pour de la perf, tu as besoin de mutĂ© in place et donc de passer en unsafe. C'est trĂšs propre car tu sais exactement oĂč c'est fait dans le code (CTRL+F "unsafe") mais il n'y a pas de magie.

      • [^] # Re: bien mais pas top

        Posté par  . Évalué à 2.

        C'est peut-ĂȘtre que je ne connais pas assez Zig, mais je trouve que Rust apporte plus (Notamment les sucres syntaxiques me semble pas vraiment plus lisible). Rust apporte aussi une programmation fonctionnelle puissante, et la sĂ©curitĂ© n'est pas rien.

        • [^] # Re: bien mais pas top

          Posté par  (site web personnel) . Évalué à 2.

          Bah ça me semble logique que Rust apporte plus, vu qu'il me semble que zig a pour but de rester un langage assez simple.
          Donc moins de maniĂšre d'exprimer la mĂȘme chose.
          Rust, c'est cool, mais les code avec 8 itérateurs chainé, ce n'est pas ce qu'il y a de plus simple ni de plus reposant à lire.

  • # Optimisation explicite ?

    Posté par  (site web personnel, Mastodon) . Évalué à 2.

    « En effet, une variable marquĂ©e du mot-clĂ© comptime sera connue Ă  la compilation. Les boucles marquĂ©es du mot-clĂ© inline seront exĂ©cutĂ©es Ă  la compilation. » Je ne vois pas cela comme un avantage. Ce genre d'optimisation, devrait ĂȘtre implicite, le compilateur reconnaissant ce qui peut se faire Ă  la compilation, sans avoir besoin d'un mot-clĂ© particulier.

    • [^] # Re: Optimisation explicite ?

      Posté par  (site web personnel) . Évalué à 3.

      Cette histoire de inline for m'a aussi encouragé à creuser, et il semblerait que le compilo fait parfois des choses inattendues.

      Par exemple en Rust, il ne faut pas utiliser la variable d'incrément dans une boucle for.

      En plus il faut prendre en compte le fait que chaque version du compilo peut changer l'heuristique, donc un code qui faisait du loop unrolling peut ne plus le faire.

      J'imagine que c'est encore pire pour le C oĂč il y a plein de compilos diffĂ©rents avec chacun leur heuristique pour faire le loop unfolding


      Bref ça semble légitime d'avoir un inline for pour s'assurer que le comportement est bien celui attendu, typiquement si on est sur une plateforme embarquée ou la place/puissance est comptée.

      • [^] # Re: Optimisation explicite ?

        Posté par  . Évalué à 3.

        Et surtout parfois, des variables peuvent ĂȘtre modifier, des boucles "innutiles" en apparences utiles
 Les bons compilateurs essayent dĂ©jĂ  de prĂ©-compiler un maximum de chose (1000*1000 sera traduit en 1000000) mais ils sont vite limitĂ©s par ce qui pourrait avoir un sens diffĂ©rent. Classiquement, l'Ă©valuation de certaines fonctions basiques peut changer.

  • # Et le parallĂ©lisme ?

    Posté par  (site web personnel, Mastodon) . Évalué à 4.

    Sauf erreur, cette dĂ©pĂȘche ne parle pas du tout de la programmation parallĂšle (une grosse faiblesse de Rust et la principale raison pour laquelle Rust ne m'intĂ©resse pas trop, par rapport Ă  Go ou Elixir). Que permet Zig dans ce domaine ?

    • [^] # Re: Et le parallĂ©lisme ?

      Posté par  . Évalué à 3.

      Je ne suis pas sûr de bien comprendre la question, mais Zig permet l'utilisation de fonctions async et fournis un certain nombre de mot-clés. Malheureusement, il y a eu une régression pour la 0.11.0 et les fonctions async ne sont pas disponibles pour cette version de Zig.

      Il y a 10 sortes de gens dans le monde – ceux qui comprennent le ternaire, ceux qui ne le comprennent pas et ceux qui le confondent avec le binaire.

      • [^] # Re: Et le parallĂ©lisme ?

        Posté par  (site web personnel, Mastodon) . Évalué à 3.

        Ça me parait un mĂ©canisme de trĂšs bas niveau. Pas de moyen d'avoir plusieurs fils d'exĂ©cution ? Comment on utiliserait ce mĂ©canisme pour, par exemple, un serveur rĂ©seau ? J'ai regardĂ© le code du serveur HTTP Zap et je ne vois absolument pas comment il fait (en tout cas, il n'utilise pas async/await).

        • [^] # Re: Et le parallĂ©lisme ?

          Posté par  . Évalué à 1.

          À ma connaissance, Zig n'inclut que ce systĂšme dans sa syntaxe, qui est mieux expliquĂ©e ici, et en quoi elle permet de faire du parallĂ©lisme : https://kristoff.it/blog/zig-colorblind-async-await/
          Mais je suppose qu'il existe des tas de librairies C et bientÎt des librairies Zig qui implémentent des fonctions beaucoup plus avancées.

          Il y a 10 sortes de gens dans le monde – ceux qui comprennent le ternaire, ceux qui ne le comprennent pas et ceux qui le confondent avec le binaire.

    • [^] # Re: Et le parallĂ©lisme ?

      Posté par  . Évalué à 7. DerniĂšre modification le 09 septembre 2023 Ă  13:43.

      En quoi le parallélisme est-il une faiblesse de rust?

      Il me semble que c'est au contraire plutĂŽt une force, avec le systĂšme de type/ownership qui Ă©vite certaines erreurs comme les data-races, la bibliothĂšque rayon ou encore async/tokio pour le io-bound.

      • [^] # Re: Et le parallĂ©lisme ?

        Posté par  (site web personnel, Mastodon) . Évalué à 4.

        La faiblesse est justement que Rust ne propose rien pour le parallélisme, uniquement des évÚnements (async/await).

        • [^] # Re: Et le parallĂ©lisme ?

          Posté par  (site web personnel) . Évalué à 6.

          Ça dĂ©pend pas mal de ce que l'on appelle parallĂ©lisme. Rayon citĂ© juste au-dessus permet de faire du parallĂ©lisme sur des donnĂ©es, ie utiliser facilement plusieurs coeurs CPU pour faire du calcul.

          Par contre, si on parle de parallĂ©lisme dans un contexte avec beaucoup d'IO, il y a async/await et tokio. Et lĂ , les avis sont beaucoup plus partagĂ©s, on va dire. Certains apprĂ©cient que Rust reste bas-niveau, tout en Ă©vitant les bugs oĂč des donnĂ©es partagĂ©es peuvent ĂȘtre lues/Ă©crites depuis plusieurs contextes d'exĂ©cution. D'autres regrettent que ça reste bas niveau et peu pratique Ă  utiliser. Il y a https://bitbashing.io/async-rust.html qui a pas mal fait parler de lui ces derniers temps.

    • [^] # Re: Et le parallĂ©lisme ?

      Posté par  (site web personnel) . Évalué à 9.

      Rust, Go & Elixir sont des langages trÚs différents, et leur approches du parallélisme est du coup différente :

      • Rust est un langage systĂšme gĂ©nĂ©raliste sans runtime par dĂ©faut (ce qui est indispensable quand on veut faire un module noyau par exemple, moins si on veut faire une petite web-app—l'absence de GC pour des gros volumes de donnĂ©es peut faire la diffĂ©rence parfois). Il permet d'avoir les performances brutes de C/C++ et des garanties fortes sur le code, mais le prix Ă  payer est en autres que le langage est clairement difficile Ă  maĂźtriser. Pour le parallĂ©lisme, on travaille de base avec des threads mais il apporte la garantie qu'on ne pourra pas modifier un mĂȘme objet depuis 2 threads sans protection (genre mutex), ce qui n'est pas le cas en Go.
      • Go se compile en natif mais vient avec un runtime pour le garbage collector et l'ordonnanceur des routines. Le langage est trĂšs simple Ă  apprendre et on code rapidement des choses qui marchent correctement, mais c'est le langage le moins expressif des 3 et il va y avoir plus de code boilerplate et moins de garanties sur l'exactitude du code qu'avec les 2 autres.
      • Elixir (je connais plus Erlang, je vais m'appuyer donc sur ce dernier, j'espĂšre ne pas dire trop de bĂȘtises) se base sur la VM Erlang, on a donc affaire Ă  quelque chose de solide pour faire des services rĂ©seaux scalables et fiables, mais au dĂ©triment de performances brutes plus faibles (VM + typage dynamique).

      Dans la mesure oĂč tu regardes du cĂŽtĂ© de Go & Elixir, mĂȘme si tu ne le prĂ©cises pas, je suppose que tu envisages ces langages dans le cadre d'un service rĂ©seau. Le parallĂ©lisme de Rust est trĂšs bien si tu veux faire un moteur 3D par exemple, mais c'est sĂ»r qu'il n'aura pas forcĂ©ment la facilitĂ© d'utilisation des goroutines/micro-processus Erlang dans des cas plus spĂ©cifiques.

      Si j'ai bien compris il existe en Rust plusieurs bibliothĂšques permettant d'utiliser async/wait (comme vu au dessus, vu les objectifs "systĂšme" du langage, imposer un runtime de base n'est pas souhaitable), mais on n'arrive pas Ă  la facilitĂ© d'utilisation des routines (on a les garanties de Rust en compensation). J'ai aussi vu passer des systĂšmes d'acteurs façon Erlang pour Rust ; ça me semble une piste trĂšs intĂ©ressante, mais tout va dĂ©pendre de la maturitĂ©/pĂ©rennitĂ© de ces briques Ă©videmment


      Pour rĂ©pondre Ă  ta question, bien que ne connaissant pas vraiment Zig, vu ses objectifs (systĂšme etc
), je suppose que sa situation doit ĂȘtre Ă  comparer Ă  celle de Rust.

      • [^] # Re: Et le parallĂ©lisme ?

        Posté par  . Évalué à 2.

        Merci pour ce commentaire pertinent.

        Je connais mal Rust (j'en ai fait un tout petit peu), mais je croyais qu'il contenait des mécanismes plus poussés pour le parallélisme.
        Dans mon souvenir, un des auteurs de Rust disait l'avoir créer justement car c'était difficile d'écrire du code parallÚle fiable en C++.

        Si je comprends bien, avec Rust on a une garantie de fiabilité, mais pas plus d'outil que ça pour écrire facilement du code parallÚle.

        J'ai beaucoup entendu parlé de lib comme tokio et les retours que j'ai pu avoir c'est que c'est trÚs puissant, mais difficile à appréhender.

        • [^] # Re: Et le parallĂ©lisme ?

          Posté par  . Évalué à 7.

          Franchement, on en fait tout une histoire de faire du code parallÚle, mais il est sacrément plus difficile de faire du code perpendiculaire.

          Là, aucun langage ne propose quoi que ce soit, surtout dÚs qu'il s'agit d'utiliser un nombre de dimensions supérieures à 2.

          À la limite, il y aurait le brainfuck:

             ,
             ,
           .+++.
             ,
             ,
          
        • [^] # Re: Et le parallĂ©lisme ?

          Posté par  . Évalué à 5.

          J'ai beaucoup entendu parlé de lib comme tokio et les retours que j'ai pu avoir c'est que c'est trÚs puissant, mais difficile à appréhender.

          C'est surtout que ça rĂ©pond Ă  des besoins trĂšs diffĂ©rents mĂȘme si le vocabulaire employĂ© se chevauche beaucoup entre les 2 usages :

          1. tokio ou async/await sont lĂ  pour avoir des traitements asynchrones ils permettent d'utiliser de maniĂšre simplifiĂ©e (et qui correspond mieux aux paradigmes du langage) aux API asynchrone. L'objectif ici est de ne pas bloquer de thread sur une attente I/O. Ça permet de gĂ©rer Ă©normĂ©ment d’interactions en consommant peu de ressources
          2. les threads par exemple sont fait pour faire du calcul intensif, l'objectif est alors de tirer le meilleur parti des différentes unités de calculs de ton CPU

          Évidement il est rare d'ĂȘtre tout l'un ou tout l'autre et il faut savoir comment les combiner pour tirer le meilleur parti pour ton besoin.

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # Nim

    Posté par  (site web personnel) . Évalué à 3.

    Zig est vraiment pas mal, surtout dans son aspect de compilation ou il permet de cross-compiler du C ce qui n'est en général pas facile.. J'ai découvert Zig pour la premiÚre fois lorsque je l'ai vu mentionner dans la communauté Nim, un autre langage dont j'ai compris que Zig était issu.

    Nim est un langage compilé qui utilise une syntaxe qui s'approche du Python, mais ce n'est pas là le mieux. Comme Zig, il a été précurseur pour l'exécution de code à la compilation et Nim est à la fois un interpréteur et un compilateur. A la différence de Zig, il possÚde par contre un systÚme de macros trÚs étendu qui permet de faire pratiquement n'importe quoi avec le langage. C'est comme du Lisp mais avec une syntaxe accessible. Et en fait, les macros sont des bouts de codes exécutés à la compilation et qui utilisent l'arbre syntaxique et peuvent le transformer presque dans n'importe quel sens.

    Nim se place un peu plus haut niveau et gĂšre les allocations et destructions mĂ©moire. Nim v1 possĂ©dait un garbage collector classique mais avec Nim v2 on passe sur un comptage de rĂ©fĂ©rence moderne (ARC) avec des optimisations pour gĂ©rer le dĂ©placement mĂ©moire et d'autres cas. Il possĂšde Ă©galement une dĂ©tection de cycles (ORC). Le gros avantage de la gestion ARC/ORC c'est que les allocations / destructions sont dĂ©terministes. TrĂšs utile pour de l'embarquĂ© ou mĂȘme lorsqu'on se soucie des performances mĂ©moire d'un programme. Et de plus cela permet de basculer progressivement vers quelque chose qui ressemble au RAII mĂȘme si la lib standard n'est pas adaptĂ©e Ă  ça encore.

  • # Mon expĂ©rience de dĂ©butant Zig

    Posté par  (site web personnel, Mastodon) . Évalué à 5.

    Cet excellent résumé m'a motivé pour essayer Zig, j'en rends compte dans cet article : https://www.bortzmeyer.org/mes-debuts-en-zig.html

Suivre le flux des commentaires

Note : les commentaires appartiennent Ă  celles et ceux qui les ont postĂ©s. Nous n’en sommes pas responsables.