Всё началось на второй паре по системному программированию. Нам дали задачу: написать CLI-утилиту для анализа логов - парсить файл, фильтровать записи по уровню ошибок, считать статистику, выводить красиво в консоль. "Ну понятно", - открыл я vim и началось мое долгое приключение...

Неделя. Две. Утилита называлась logz, она умела читать логи nginx и apache, фильтровать по уровню (DEBUG, INFO, WARN, ERROR), по дате, по IP, выводила топ адресов с наибольшим числом ошибок, рисовала простенький bar-chart прямо в терминале через unicode-символы. Только вот я сидел как-то вечером, запустил wc -l main.c - 3147 строк. И смотрел на это число минуты три с таким лицом - O_O.

Сама утилита работала. Но открывая её осознаешь что - это месиво. Одна функция process_file на 400 строк. Сегфолты раз в неделю. Valgrind как лучший друг. И каждый раз когда надо добавить фичу - сначала полчаса вспоминаешь что вообще происходит в коде.

Потом я случайно прочитал пост про Zig на lobste.rs. Заинтересовался и попробовал. Через месяц у меня была та же утилита, но теперь на 1089 строках, которая работала быстрее и не падала.

Тут я понял что вот золотая жила и расскажу о том - зачем Zig, как переписывал, где облажался и что вышло в итоге.

Постановка проблемы: что конкретно бесило в C

Я не скажу что "C - это плохой язык", потому что это чушь. Но конкретно мои боли в этом проекте:

Память вручную. Каждый malloc - это обязательство. Написал функцию, она вернула строку - не забудь освободить. Добавил ранний return по ошибке - не забудь освободить до него. Я честно раза три ловил утечки памяти в logz. Valgrind спасал, но ты не запускаешь valgrind - ты просто пушишь.

Сразу скажу что сегфолты - это когда прога пытается залезть в память к которой ей нельзя.

Сегфолты. Это отдельная история. Утилита читала файлы гигабайтами, и где-то в середине - Segmentation fault (core dumped). Дебажишь час, находишь: буфер на 256 байт, а строка лога оказалась 300. Классика. Починил - появился другой: off-by-one в парсере дат. Я реально начал видеть сегфолты во сне.

Bil-система. Мой Makefile работал, пока я его помнил. Через месяц открываешь - что это вообще такое? Плюс make иногда говорит «up to date» хотя ты только что изменил хедер. Да, знаю про -MMD, но это ещё один уровень вещей которые надо держать в голове.

Объём кода. Элементарный парсер строки вида 2026-01-17 ERROR [nginx] 192.168.1.100 500 на C - это функция строк на 50. Потому что: strtok или ручной парсинг, проверки NULL на каждом шагу, strncpy с ограничением длины, освобождение временных буферов... Это не сложно, но это много кода ради простой вещи.

Обработка ошибок. Функции возвращают -1 или NULL при ошибке, смысл ошибки - в errno или нигде. Пишешь цепочку if (result < 0) { perror("..."); return -1; } и либо ты теряешь контекст, либо таскаешь его через все уровни вручную. 3000 строк такого кода - это уже нечитабельно.

Почему Zig, а не Rust?

Rust читал месяц. Крутой, но borrow checker для утилиты которая читает файл и пишет в stdout - избыточен. Хотел C по духу, но без боли.

Zig зашёл за:

  • Нет hidden control flow - defer file.close() прямо в коде, ничего за кулисами

  • Ошибки через типы - !T значит "либо значение, либо ошибка", компилятор не даст проигнорировать

  • Кросс-компиляция из коробки - zig build -Dtarget=x86_64-windows без тулчейнов

  • Маленький проект собирается за секунду

Кратко: C - это контроль ценой боли, Rust - это безопасность ценой сложности, Zig - это C, только с нормальными инструментами.

Архитектура

logz - CLI для анализа access.log / error.log:

logz --file access.log --level ERROR --from 2026-01-17 --top-ip 10

Pipeline из пяти модулей, данные текут строго вниз:

В C у меня было иначе: одна функция process_file делала всё сразу. Добавить новый формат лога = переписывать её всю

Код: Zig рядом с C

1.Парсинг аргументов

С +- 80 строк было

// args C
int parse_args(int argc, char **argv, Config *cfg) {
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--file") == 0) {
            if (i + 1 >= argc) {
                fprintf(stderr, "error: --file requires a value\n");
                return -1;
            }
            cfg->filename = argv[++i];
        } else if (strcmp(argv[i], "--level") == 0) {
            if (i + 1 >= argc) return -1;
            cfg->level = parse_level(argv[++i]);
            if (cfg->level < 0) {
                fprintf(stderr, "error: unknown level\n");
                return -1;
            }
        } else if (strcmp(argv[i], "--top-ip") == 0) {
            if (i + 1 >= argc) return -1;
            cfg->top_ip = atoi(argv[++i]);
            if (cfg->top_ip <= 0) return -1;
        }
// еще параметры
    }
    return 0;
}

Zig-версия - стало +- 40 строк

// args.zig
const Config = struct {
    filename: []const u8 = "",
    level: ?LogLevel = null,
    top_ip: u32 = 10,
    from_date: ?i64 = null,
};

fn parseArgs(allocator: std.mem.Allocator) !Config {
    var args = try std.process.argsWithAllocator(allocator);
    defer args.deinit();

    var cfg = Config{};
    _ = args.next(); // пропускаю имя программы

    while (args.next()) |arg| {
        if (std.mem.eql(u8, arg, "--file")) {
            cfg.filename = args.next() orelse return error.MissingValue;
        } else if (std.mem.eql(u8, arg, "--level")) {
            const lvl = args.next() orelse return error.MissingValue;
            cfg.level = try LogLevel.parse(lvl);
        } else if (std.mem.eql(u8, arg, "--top-ip")) {
            const n = args.next() orelse return error.MissingValue;
            cfg.top_ip = try std.fmt.parseInt(u32, n, 10);
        }
    }
    return cfg;
}

orelse return error.MissingValue - одна строка вместо трёх проверок. Ошибка явная, не -1.

2.Парсинг строки лога

С-версия +-65 строк

// parser.С
LogEntry *parse_line(char *line) {
    LogEntry *entry = malloc(sizeof(LogEntry));
    if (!entry) return NULL;

    char *saveptr;
    char *token = strtok_r(line, " ", &saveptr);
    if (!token) { free(entry); return NULL; }

    if (parse_timestamp(token, &entry->timestamp) != 0) {
        free(entry); return NULL;
    }

    token = strtok_r(NULL, " ", &saveptr);
    if (!token) { free(entry); return NULL; }

    entry->level = level_from_string(token);
    if (entry->level == LOG_LEVEL_UNKNOWN) {
        free(entry); return NULL;
    }

    // ещё 40 строк в том же духе
    return entry;
}
// Важно: вызывающий обязан вызвать free(entry) потом

Zig-версия +- 35-40 строк

// parser.zig
const LogEntry = struct {
    timestamp: i64,
    level: LogLevel,
    source: []const u8,
    ip: []const u8,
    status: u16,
};

fn parseLine(line: []const u8) !LogEntry {
    var iter = std.mem.splitScalar(u8, line, ' ');

    const ts_str  = iter.next() orelse return error.InvalidFormat;
    const timestamp = try parseTimestamp(ts_str);

    const level_str = iter.next() orelse return error.InvalidFormat;
    const level = try LogLevel.parse(level_str);

    const source = iter.next() orelse return error.InvalidFormat;
    const ip     = iter.next() orelse return error.InvalidFormat;

    _ = iter.next(); // пропускаю HTTP-метод и путь
    const status_str = iter.next() orelse return error.InvalidFormat;
    const status = try std.fmt.parseInt(u16, status_str, 10);

    return LogEntry{
        .timestamp = timestamp,
        .level     = level,
        .source    = source,
        .ip        = ip,
        .status    = status,
    };
}

Чем лучше: нет malloc, нет free, нет strtok с его глобальным состоянием. Функция либо возвращает валидный LogEntry, либо ошибку - и это написано прямо в сигнатуре !LogEntry. Вызывающий не может случайно забыть освободить память.

3.Работа с файлами и defer

// main.zig
pub fn processFile(
    allocator: std.mem.Allocator,
    path: []const u8,
    cfg: Config,
) !Stats {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close(); // закроется при любом выходе из функции

    var buffered = std.io.bufferedReader(file.reader());
    var reader = buffered.reader();

    var stats = Stats.init(allocator);
    defer stats.deinit();

    var line_buf: [8192]u8 = undefined;

    while (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) |line| {
        const entry = parseLine(line) catch continue; // битые строки просто пропускаем
        if (cfg.matches(entry)) {
            try stats.add(entry);
        }
    }

    return stats;
}

В C мне надо было писать fclose(file) перед каждым return, а их у меня было четыре разных в этой функции. Каждый раз думать: "я закрыл файл?". С defer эта задача исчезла.

Бенчмарки

Тестовый файл: 5 млн строк, +-800 МБ.

Метрика

С

Zig

Время выполнения

4.2 сек

2.8 сек

Размер бинарника

47КБ

32КБ

Пиковая память

124МБ

89МБ

Время сборки

3.1 сек

1.4 сек

Строк кода

3147

1089

Zig быстрее на треть - просто потому что я сразу написал нормальный bufferedReader, а в C у меня был fgets без буферизации. Строк кода в три раза меньше - это главный результат.

"Грабли"

Allocators непривычны. Поначалу лепил везде page_allocator. Потом открыл ArenaAllocator - для CLI-утилиты идеально: выделяешь сколько хочешь, в конце arena.deinit() и всё сразу освобождается.

Строки - слайсы, не char*. []const u8 не имеет нуля в конце. Передаёшь в C-функцию напрямую - UB. Надо .ptr и убедиться что строка null-terminated.

Ошибки компилятора длинные. Сообщение на 20 строк - читай только первую, остальное трассировка. Уже привыкаешь.

Язык меняется. Zig пока не 1, я на 0.13. Примеры из интернета 2022 - 23 года часто не компилируются - всегда смотри официальную документацию.

Выводы по Zig

Стоит ли переходить на Zig в 2026?

Естественно да, если: пишешь CLI-утилиты, системные инструменты, что-то встраиваемое. Нужен контроль над памятью, но без сегфолтов каждую неделю. Хочешь кросс-компиляцию без боли. Устал от Makefile.

Лучше остаться на C, если: работаешь с большой существующей C-кодовой базой и интеграция не нужна. Или нужна максимальная стабильность языка - пока не 1, API может меняться между версиями.

Лучше взять Rust, если: проект большой и долгоживущий, важна экосистема (cargo + crates.io несравнимо богаче), работаешь в команде где Rust уже знают.

Для меня Zig занял нишу: "хочу писать низкоуровневый код без страданий". Три месяца назад я смотрел на 3000+ строк C и думал - ну это всё. Сейчас у меня 1089 строк Zig, утилита работает быстрее, и я понимаю каждую строку кода.

Если хочешь попробовать - ziglang.org. Начни с zig init, напиши hello world, потом попробуй открыть и построчно прочитать файл. Язык небольшой, стандартная библиотека читается за вечер.

Вот и все, напишите в комментах если было интересно.Буду рад если кому-нибудь это поможет и он будет пользоваться напостоянке.

Комментарии (49)


  1. DSoap
    05.04.2026 11:33

    Хм.. Как будто для такой задачи go может еще лучше подойти, что думаете?


    1. gev
      05.04.2026 11:33

      Haskell же!


    1. Siemargl
      05.04.2026 11:33

      Тоже варианты (D, Go, Rust)

      Еще есть c# aot, который специально для этого. И вполне эффективный Nim, если нра Питон - синтаксис.


  1. alexsey_p
    05.04.2026 11:33

    Неплохая статья


  1. Siemargl
    05.04.2026 11:33

    1. Удивлён, что версия на Zig получилась короче - он более многословный.

    2. Для парсинга аргументов на С стоило тоже взять готовую библиотеку. Сравнение было бы корректнее

    3. На С тоже элементарно делается defer макросами

    Но все же Zig это язык уже нового поколения.

    И вообще - это перевод? Потому что актуальный Zig 0.16


    1. Wi11k4
      05.04.2026 11:33

      Почему для С ТОЖЕ стоило взять готовую библиотеку? Вроде в zig все решено с помощью std. И где вы видели актуальный Zig 0.16? На официальном сайте указана версия 0.15.2


      1. Siemargl
        05.04.2026 11:33

        Чтобы сравнивать одинаковые подходы в длину кода.

        В даунлоадз и у себя на компе 0.16


    1. atd
      05.04.2026 11:33

      Я вот на 0.14 сижу и доволен ;) Ну либо автор написал код какое-то время назад, а статью только сейчас.


  1. evgeniy_kudinov
    05.04.2026 11:33

    К сожалению до релиза 1.0 далеко, когда выйдет уже и не нужен будет наверное.


    1. MainEditor0
      05.04.2026 11:33

      Так ему уже 10 лет, они его полируют, ломают (-ли) совместимость, чтобы к релизу выдать что-то готовое... Не уверен насчёт достоверности, но, вроде, в этом году выйдет стабильный релиз v1.0


      1. evgeniy_kudinov
        05.04.2026 11:33

        LLM выдала со ссылкой на Telegram-канал: "По некоторым прогнозам, версия 1.0 языка программирования Zig выйдет не раньше 2029 года".

        Я читал, что автор - перфекционист и стремится создать надёжный инструмент. Однако процесс версионирования может затянуться на следующие 10 лет. За это время многое может измениться в разработке ПО, учитывая быстрый прогресс LLM.

        Если у вас есть точная информация о дате выхода версии 1 из roadmap, буду признателен.


  1. garm
    05.04.2026 11:33

    Язык С не прибит гвоздями к Makefile, там уже много разных альтернатив придумали.

    И мне интересно, почему zig , а не c++?


    1. domix32
      05.04.2026 11:33

      Чтобы не страдать с Makefile, cmake, automake, premake и прочими сборочными извращениями, которые современные языки решают обычной сабкомандой init. Уж лучше какой-нибудь cppfront взять.


      1. Siemargl
        05.04.2026 11:33

        Ох уж эти страдатели, которые маке не осилили)


        1. gev
          05.04.2026 11:33

          Даже и пытаться осиливать не стали, разу взяли Shake ;)


        1. domix32
          05.04.2026 11:33

          Вот его-то как раз проще всего освоить. В сравнении с Zig, который требует только Zig для контроля зависимостей это определённо жирный минус. А уж если один пишет на cmake, второй на autotools, а третий либу собирает через conan веселье умножается пропорционально и можно шутить что неосиливатель маке не осилил ещё каких-то три-четыре сборочных системы. Это ж куда важнее, чем просто писать прогамму на языке.


          1. Siemargl
            05.04.2026 11:33

            Кстати, попадаются проекты на С, которые используют Zig чисто как систему сборки.

            Он ещё и кэширует (в принципе, так же как и маке) сборку автоматически.

            А вот с подгрузкой сторонних пакетов лично не пробовал, но вроде возможности есть.

            Так что в плюс. Но каких то особенных прорывов нет.


            1. domix32
              05.04.2026 11:33

               используют Zig чисто как систему сборки.

              Это как раз один из их способов саморекламы. Многие хвалят компилятор Си у zig за понятные ошибки да и вообще за удобную интероперабельность с Си. Похожую поддержку встречал только у D. Одно время build.zig пытались контрибутить во многие популярные репы на си. Кто-то принял, кто-то послал на три буквы, чтобы не заниматься поддержкой ещё одного языка в проекте. Слышал, что некоторые сишные проекты решили использовать целиком инфраструктуру zig для сборки кода, но живьем не встречал.


  1. durnoy
    05.04.2026 11:33

    Rust читал месяц. Крутой, но borrow checker для утилиты которая читает файл и пишет в stdout - избыточен.

    Так ведь если утилита в одном исходном файле, то borrow checker вы даже не заметите.


  1. SpiderEkb
    05.04.2026 11:33

    В C мне надо было писать fclose(file) перед каждым return, а их у меня было четыре разных в этой функции.

    Множественный return вообще не очень хорошая практика. Корректнее использовать единую точку выхода.

    К сожалению, в языках где нет возможности определять блок

    on-exit;
      // выполняем все необходимые перед выходом действия
      ...
      return;

    куда передается управление поде каждого return в функции, или хотя бы поддержки сабрутин (аналог подпрограммы в бейсике - отдельный блок кода внутри функции, вызываемый без создания нового уровня стека и не имеющий собственных аргументов и локальных переменных), позволяющий организовать точку выхода

    begsr srExit;
      // выполняем все необходимые перед выходом действия
      ...
      return;
    endsr;

    и позволяющий вместо

    if (...) return;

    использовать

    if (...) exsr srExit;

    использование единой точки выхода может потребовать определенной организации кода для избежания многоуровневых if !error.

     Функции возвращают -1 или NULL при ошибке, смысл ошибки - в errno или нигде.

    Это, мягко говоря, не так. Есть понятие "структурированной ошибки". В простейшем виде выглядит вот так:

     typedef struct Qus_EC
        {
           int  Bytes_Provided;
           int  Bytes_Available;
           char Exception_Id[7];
           char Reserved;
           char Exception_Data[];           /* Varying length        */
        } Qus_EC_t; 

    Передается аргументом в функцию. На выходе достатчоно проверить

    if (Qus_EC.Bytes_Available) {
      // функция заполнила ошибку
      ...
    };

    Bytes_Provided тут - размер структуры (определяется размером блока дополнительных данных Exception_Data) ; Bytes_Available = 0 - ошибки не было, Bytes_Available > 0 - ошибка была; Exception_Id - код ошибки; Exception_Data - блок дополнительных данных произвольного размера, связанных с конкретной ошибкой.

    Остальное тоже весьма спорно. Например

    В C у меня было иначе: одна функция process_file делала всё сразу. Добавить новый формат лога = переписывать её всю

    А зачем было делать именно так? Почему не разбить эту функцию на несколько более мелких? Например, "диспетчер", определяющий формат лога + набор парсеров под разные форматы? Новый формат - пишем парсер и добавляем его в таблицу парсеров диспетчера.

    Такое ощущение, что кинулись писать на С особо не продумав что и как, поплясали с бубном на граблях и потом уже, получив понимание что и как, переписали все на Zig. И 90% "бонусов" не от того, что Zig лучше, а от пришедшего понимания тонкостей задачи.


    1. ValeryIvanov
      05.04.2026 11:33

      Это всё круто, но как ты ошибки не возвращай, всегда будет одна проблема - компилятор не скажет, где ты забыл обработать ошибку. Один раз не проверил что вернула функция, у тебя с большой вероятностью сегфолт, который ещё попробуй исправь. zig такого не позволяет. Хотя, на самом деле, zig тоже не очень безопасный в этом плане. Забыл освободить ресурс через defer и прога падает или память утекает, но в нём хотя бы есть DebugAllocator и стек вызовов, что позволяет ошибки отловить достаточно рано. То есть на корню все проблемы системного программирования zig не решает, но он однозначно делает многие вещи лучше C.


      1. SpiderEkb
        05.04.2026 11:33

        Ну я не спорю. Писать на С требует тренировки мозга - многое приходится в голове держать. Это только годами практики нарабатывается до автоматизма.

        В этом плане Rust интереснее, хотя и требует привыкания к заложенным в нем концепциям.

        А структурированная ошибка - это целая концепция на уровне системы.

        Есть т.н. message file - таблица где содержится код ошибки, ее текстовка (с возможностью подстановки параметров), уровень серьезности (например, 0 - информация, 10 - предупреждение, 20 - критическая). Есть системное API куда передается вот эта структура, содержащая код и значения параметров для подстановки. API по коду находит в message file данную ошибку, берет ее текстовку, подставляет параметры и возвращает полный текст и уровень серьезности.

        Разрабатывая сое ПО вы всегда можете расширять message file добавляя туда характерные для вашего ПО ошибки со своими кодами. Функция в общем случае возвращает получилось/не получилось. Если не получилось, вы можете или анализировать ошибку по коду, или же дернуть API и получить полный текст (уже с подставленными параметрами) и дальше выводить его на экран, в лог или еще куда вам нужно.

        Плюс эта же ошибка может пробрасываться в виде исключения (языкового или системного, если таковые поддерживаются).


        1. domix32
          05.04.2026 11:33

          Как-то так по итогу оно и вываливается во втрое больший код, потому что сначала нужно написать систему, чтобы было хорошо - то арену добавь, то работу с массивами скопипасти, то обработку ошибок допиши...


          1. SpiderEkb
            05.04.2026 11:33

            Если речь идет о долгосрочном проекте который потом придется поддерживать и развивать, то оно того стоит.

            Ну или если себе строишь набор рабочих инструментов на долгое время.

            И в любом серьезном проекте работа с ошибками занимает достаточно большую часть. Если не делать как современные вебклепатели - на все одна ошибка "что-то пошло не так, попробуйте как-нибудь потом"


            1. domix32
              05.04.2026 11:33

              Если речь идет о долгосрочном проекте который потом придется поддерживать и развивать, то оно того стоит.

              Из чего следует необходимость учить ещё один какой-нибудь язык для того чтобы собирать код на си. Можно конечно использовать какой-нибудь nob, чтобы иметь только компилятор в зависимостях, но там свои квирки и segafult приветы будут.


              1. SpiderEkb
                05.04.2026 11:33

                Ну если по хорошему, то использовать более одного языка в одном проекте - нормально.

                Я вот пишу как минимум на двух (то, что в прод идет). Ну и Rust немного для себя... Правда, языки у меня сильно разные. Один хорош для работы с БД и всяких коммерческих расчетов (это основной), но если что-то более низкоуровневое, то там С/С++ лучше.

                Часто бывает что один кусок задачи лучше и быстрее решается на одном языке, другой - на другом.


    1. d3d12
      05.04.2026 11:33

      Одна точка выхода:

      if (error1) goto one_exit;
      ...
      if (error2) goto one_exit;
      ...
      if (error3) goto one_exit;
      ...
      one_exit:
      ...release actions...

      Если кому то режет глаз goto, то:

      while (TRUE) {
        if (error1) break;
        ...
        if (error2) break;
        ...
        break;
        }
      ...release actions...

      Есть еще вариант с исключениями, но лично я их использование считаю дурным тоном.


      1. SpiderEkb
        05.04.2026 11:33

        Есть еще вариант с исключениями, но лично я их использование считаю дурным тоном.

        Да, согласен. Исключения штука ресурсоемкая. В высоконагруженных системах использование их стоит свести к минимуму. Только в случае крайне необходимости.

        Когда условий много делал примерно так:

        for (stage=0; stage < MAX_STAGE; stage++) {
          switch (stage) {
            case 1:
              ... блок операций где может возникнуть прерывающая ошибка ...
              break;
        
            case 2:
              ... блок операций где может возникнуть прерывающая ошибка ...
              break;
        
            ...
          }
        
          if (error) break;
        }

        Чуть хуже с точки зрения производительности, но лучше с точки зрения читаемости. Плюс то, что находится в блоках case вынести в отдельные функции

        Но суть, да, в том чтобы обеспечить единую точку входа и единую точку выхода.


        1. AlexSky
          05.04.2026 11:33

          do {
              ... блок операций где может возникнуть прерывающая ошибка ...
              break;
          
              ... блок операций где может возникнуть прерывающая ошибка ...
              break;
          } while (0);

          А почему не так?


          1. SpiderEkb
            05.04.2026 11:33

            Потому что вам придется после каждого блока проверять if (error) break;

            А здесь на каждом обороте цикла отработает один блок (тот, что идет по соотв. номеру) и после этого проверится ошибка.


  1. Weselton
    05.04.2026 11:33

    Наброшу немного за Rust

    1)

    Ошибки через типы - !T

    В Rust еще круче есть - Result<T>: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#recoverable-errors-with-result

    2)

    Кросс-компиляция из коробки - zig build -Dtarget=x86_64-windows без тулчейнов

    В Rust: https://crates.io/crates/cargo-zigbuild

    3)

    Маленький проект собирается за секунду

    В Rust - за доли секунд

    4)

    Rust - это безопасность ценой сложности

    Это ложно. Корректный код на Rust в миллиарды раз проще писать, чем на C, Zig и, даже, Go. Проверено

    5)

    1089 строк на Zig

    На Rust вышло бы и того меньше за счет отсутствия defer (это "фичей" назвать трудно, если честно; только в ироничном ключе), функционального подхода и удобнейших инструментов для работы со строками

    6)

    Ошибки компилятора длинные

    В Rust прекрасные ошибки от компилятора, часто содержащие примеры исправлений. На зависть многим языкам

    7)

    Стоит ли переходить на Zig в 2026?

    Почти в любом случае - нет, железобетонно. Нужен язык, использовать который можно без боли, быстрый, без gc - Rust, без серьезных альтернатив

    Рекомендую, если целью является обучиться крутому инструменту production-ready и стать востребованным разработчиком, изучить Rust и/или Go. C - deprecated, Zig - тупик.

    ИМХО

    PS: вспомнилось мне интервью на youtube-канале ThePrimeTime с Алексеем Кладовым о Zig и его фичах. Зашла речь про то, что Алексею понадобилось быстренько написать небольшой инструмент для работы с большим строковым файлом. Он ответил, что выбрал Rust. Очень показательно )
    https://www.youtube.com/watch?v=V8Bg55lTUCw&t=3701s


    1. SpiderEkb
      05.04.2026 11:33

      С Rust есть "всего лишь одна маленькая проблема" - нужно вникнуть в заложенные в нем концепции, принять их, немного перестроить мозги. И после этого работа с ним становится простоя и естественной.

      Но на это требуется время, сразу с наскока может не получиться - пока не проникнешься, все будет казаться мучительно и больно.


    1. domix32
      05.04.2026 11:33

      На Rust вышло бы и того меньше за счет отсутствия defer

      Скорее всего не стало бы меньше а +/- столько же, сколько и у Zig, если писать единым rs. Обработка result наверняка заполнила эти пробелы.


  1. AngryEvilCookie
    05.04.2026 11:33

    Парсил несколько гигов хмлников на шарпе за 5 секунд, от проца и ио конечно зависит, но чистый си это всегда геморрой конечно. Не знаю кто такой зиг, но если бы дожал на си, было бы быстрее.


    1. Siemargl
      05.04.2026 11:33

      Zig тоже LLVM. Так что по компиляции будет паритет


  1. okhsunrog
    05.04.2026 11:33

    Читаю статью - и мозг постоянно цепляется за нейросетевые паттерны текста. Автор, признайся честно - нейросетка писала статью?


    1. zum
      05.04.2026 11:33

      Очевидно, что это вывод LLM. А так как автор студент, то меня преследует смутное сомнение, что это один из тех случаев, когда преподаватель ставит задачу "написать на хабр". Сколько раз так уже было.

      Страшная, конечно, смесь - студент и LLM.


  1. UFO_01
    05.04.2026 11:33

    пишешь CLI-утилиты, системные инструменты, что-то встраиваемое

    Ну не знаю, не знаю, как-то сомнительно если честно. Для перового и второго лучше взять что-то из троицы C++, Rust, Go. Для встраиваемых систем можно использовать Rust если уже пишешь на нём, если же до этого писал на C/C++, смысла переходить особо нет, максимум какую головную боль вылечишь - безопасный код писать можно, но ты не обязан.

    И моя личная головная боль - сертификация по EN 50128, где по факту C это стандарт, он просто самый предсказуемый и проверяемый, и сертификация под него дешевле, а Ferrocene пока не дотягивает. Тем более что зачастую есть только компилятор для C99, о существовании C++ разработчики не слышали, и приходится терпеть терпеть, как например для 8-16-разрядных контроллеров. А с учётом жизненного цикла кода в 25 лет, Rust или что-то другое я увижу не скоро.


  1. Siemargl
    05.04.2026 11:33

    сертификация по EN 50128

    C23 самую большую дырку с массивами закрыл. И с многопоточкой поработали

    Он раньше пройдёт все сертификации.

    А Раст туда попадет не раньше чем no-panic и nostd стандартизуют

    Zig выигрывает у многих для эмбеда за счёт идеологии - никаких скрытых действий (аллокаций, паник, исключений итп) и кросс компиляции из коробки.


  1. FlyingDutchman2
    05.04.2026 11:33

    А если переписать эту утилиту с zig на Python, получится еще в два раза меньше строк кода.


    1. domix32
      05.04.2026 11:33

      И время работ с гигабайтными файлами сократится с пары секунд до жалких минут, да.


    1. d3d12
      05.04.2026 11:33

      И производительность упадет раз так в 1000.


      1. FlyingDutchman2
        05.04.2026 11:33

        Интересный вопрос. Думаю, не в 1000 раз, а меньше.


        1. d3d12
          05.04.2026 11:33

          Куда уж меньше ))
          Если серьезно, Python по бенчмаркам в 10 раз медленнее PHP, а это оба интерпретируемые языки, куда им до скомпилированного машинного кода.


          1. Siemargl
            05.04.2026 11:33

            Плюсовый иострим может с этим поспорить =)


  1. kukovik
    05.04.2026 11:33

    Сразу скажу что сегфолты - это когда прога пытается залезть в память к которой ей нельзя.

    Тут надо уточнить, что если сегфолта нет (то есть прога залезла в память, к которой ей можно), это все равно не исключает ошибки. Потому как не факт, что это именно та часть памяти, которая подразумевалась.


  1. agmt
    05.04.2026 11:33

    • Нет hidden control flow - defer file.close() прямо в коде, ничего за кулисами

    В языках без gc деструктор выглядит безопаснее и понятнее


    1. Siemargl
      05.04.2026 11:33

      Язык без GC и объектов. Хм, дайте подумать....

      Приятнее, да=) для птиц


      1. SpiderEkb
        05.04.2026 11:33

        На самом деле gc нужен там, где есть активная работа с динамической памятью. Которая в целом отрицательно сказывается на производительности есть это высоконагруженная система (чтобы хоть как-то это нивелировать начинаются пляски с кастомными аллокаторами).

        Поэтому языков, где динамическая работа с памятью сведена к минимуму, хватает. И GC там просто не нужен.