Why is Zig so cool? 🔥 Горячее 💬 Длинная дискуссия
Zig - это не просто замена C или C++, а совершенно новый подход к программированию, который удивил автора с 45-летним опытом. Самые впечатляющие особенности языка - встроенная возможность компилировать C-код и кросс-компиляция "из коробки", что уже оказывает значительное влияние на индустрию.
Установка компилятора Zig проста и доступна для различных платформ и архитектур на официальном сайте. Автор подчеркивает, что эти функции сами по себе уникальны, но сосредоточен на том, как программировать на Zig и почему стоит выбрать его вместо других языков.
Комментарии (467)
- Статья преувеличивает инновативность Zig, не подкрепляя заявления о "революционности" реальными уникальными фичами.
- Пользователи отмечают практические преимущества: простота установки (через PyPI), кросс-компиляция, явный синтаксис и компиляция C-кода.
- Критика включает отсутствие безопасности памяти, спорные решения (политика идентификаторов, отсутствие данных в ошибках) и сравнение с Rust/Ada как более зрелых альтернатив.
- Отдельные хвалят метапрограммирование (comptime), простоту навигации по коду и удобство для низкоуровневого программирования.
- Обсуждение подчеркивает субъективность восприятия: для одних Zig "меняет подход к программированию", для других — лишь "улучшенный C" без уникального позиционирования.
I'm too dumb for Zig's new IO interface 💬 Длинная дискуссия
Zig 0.15 сменил IO: std.Io.Reader и std.Io.Writer.
Старый способ тормозил и путался из-за anytype.
Подключаемся к www.openmymind.net:443:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.DebugAllocator(.{}).init;
defer gpa.deinit();
const a = gpa.allocator();
const s = try std.net.tcpConnectToHost(a, "www.openmymind.net", 443);
defer s.close();
var wbuf: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
var rbuf: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
var w = s.writer(&wbuf);
var r = s.reader(&rbuf);
var bundle = std.crypto.Certificate.Bundle{};
try bundle.rescan(a);
defer bundle.deinit(a);
var tls_buf: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
var tls = try std.crypto.tls.Client.init(
r.interface(),
&w.interface,
.{
.ca = .{ .bundle = bundle },
.host = .{ .explicit = "www.openmymind.net" },
.read_buffer = &.{},
.write_buffer = &tls_buf,
},
);
defer tls.end() catch {};
try tls.writer.writeAll("GET / HTTP/1.1\r\n\r\n");
var out: [1024]u8 = undefined;
var fw = std.Io.Writer.fixed(&out);
const n = try tls.reader.stream(&fw, .limited(out.len));
std.debug.print("read: {s}\n", .{out[0..n]});
}
Ключевые моменты
stream.reader()иwriter()требуют буфер.- Чтобы передать их в
tls.Client, нужно.interface()и&interface. tls.Client.initобязательно проситca,host,read_buffer,write_buffer.- Чтение ответа:
tls.reader.streamвstd.Io.Writer.
Комментарии (161)
- Автору пришлось вручную вызывать flush у двух обёрток и разбираться с тем, что первое чтение всегда возвращает 0.
- Основная жалоба — отсутствие документации и примеров; сообщество отвечает «читай исходники», но участники признают, что API ещё нестабилен.
- Некоторые считают новый I/O-интерфейс элегантным, но сложным даже для «Hello, world!»; другие предпочитают прямое использование системных API.
- Zig позиционируется как язык системного программирования, близкий к C, с преимуществами вроде discriminated unions и comptime, но сопровождается постоянными breaking changes.
Комментарии (89)
- Zig вызывает интерес благодаря мощному comptime и «inline else», позволяющим абстрагироваться без рантайм-оверхеда.
- Участники сравнивают его метапрограммирование с C, D и Rust, отмечая, что похожие идеи уже были, но Zig может сделать их популярнее.
- Главный упрек Zig — отсутствие гарантий memory- и data-race safety, из-за чего многие считают его неподходящим для многопоточного кода.
- «comptime unreachable» воспринимается как способ доказать компилятору недостижимость кода, а не как runtime-assert.
- Некоторые считают, что язык ещё нестабилен и экосистема незрела, поэтому широкое внедрение отложено.
Zig Error Patterns
Введение
Я часто использую отладчик, но привык и к выводной отладке, особенно в юнит-тестах. Хотелось улучшить её и чаще подключать отладчик.
Улучшение выводной отладки
Главная проблема — «шум»: в цикле интересна одна итерация, а печатается всё. Или удобнее читать форматированную структуру, но приходится раскидывать print’ы по коду. В Zig тесты используют error’ы, значит можно печатать только при падении теста через errdefer:
test { errdefer std.debug.print("{f}", .{ast}); // ... }
Так контекст появляется только при ошибке, без засорения лога.
Запуск тестов в отладчике
Просто запустить seergdb или gdb -tui неудобно: тестовые бинарники лежат в zig-cache. Трюк из ziggит: build.zig может запускать команды и передавать путь артефакта:
// seergdb — GUI фронтенд для gdb const debugger = b.addSystemCommand(&.{ "seergdb", "--run", "--" }); debugger.addArtifactArg(exe_unit_tests);
const debug_step = b.step("debug", "Run unit tests under debugger"); debug_step.dependOn(&debugger.step);
Это запускает правильный бинарник. Но отладчик сработает лишь на брейкпоинте или панике, тогда как раннер тестов «проглатывает» ошибки.
Комбинация трюков
Добавим @breakpoint через errdefer:
test { errdefer @breakpoint(); }
Так мы попадаем в точку ошибки, видим контекст и вывод std.testing.expect*. Минус: при zig build test отчёт показывает падение всего шага тестов, а не отдельных кейсов. Нужна возможность включать брейкпоинты выборочно.
Условная компиляция
Через build options пробрасываем флаг, решающий, вызывать ли @breakpoint в тестах.
Минимальный скрипт сборки, запускающий тесты, дополняем опциями:
const std = @import("std");
pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{});
const lib = b.addModule("zig-test-patterns", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const options = b.addOptions();
options.addOption(bool, "debugger", false);
lib.addImport("config", options.createModule());
const mod_tests = b.addTest(.{ .root_module = lib });
const run_mod_tests = b.addRunArtifact(mod_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
}
В коде тестов:
const std = @import("std"); const config = @import("config");
test "errdefer @breakpoint()" { errdefer if (config.debugger) @breakpoint(); return error.FixMe; }
test "no breakpoint" { return error.FixMe; }
zig build test — без брейкпоинтов. Но менять значение флага так — значит пересобирать build.zig. Добавим опцию прямо в систему сборки:
var options = b.addOptions(); const use_debugger = b.option( bool, "debugger", "Enables code intended to only run under a debugger", ) orelse false; options.addOption(bool, "debugger", use_debugger);
Теперь можно переключать поведением командой:
zig build -Ddebugger test
И, при желании, привязать шаг запуска отладчика к этому флагу.
Комментарии (46)
- Участники хвалят согласованность базовых конструкций Zig: минимализм синтаксиса и мощь comptime позволяют элегантные решения без излишней сложности.
- Особый интерес вызвал errdefer: многие отмечают, что это упрощает тесты и отладку; звучит мнение, что такую возможность «стоит иметь каждому языку».
- Обсуждают практики отладки: полезны советы по интеграции дебаггера в build.zig, что избавляет от ручного поиска исполняемого файла в кэше.
- Поднимается вопрос об ошибках без полезной нагрузки в Zig: при парсинге (например, JSON) типовые ошибки вроде UnexpectedToken недостаточно информативны; интересуются паттернами передачи дополнительного контекста.
- Есть замечание о смешении стилей именования (camelCase в stdlib vs snake_case у автора), что может сбивать с толку.
- Отмечают эстетику сайта и блога: шрифты (Berkeley Mono), цветовую схему и ретро-оформление — «как в старых DOS-играх».
- Проводится параллель с D: аналогичная идея реализована через scope(failure), что подчеркивает общность концепции.