Сравнение с ручной памятью C и Rust

Ставим подход Zig к памяти рядом с C и Rust, чтобы понять его нишу.

Ручная безопасность в Zig — это компромисс: язык не доказывает корректность памяти, как Rust, но делает выделения и освобождения настолько явными и инструментированными, что ошибки легко находить.

У трёх системных языков три разные философии памяти. C даёт полный ручной контроль без страховки. Rust доказывает безопасность памяти на этапе компиляции ценой строгих правил. Zig занимает середину: ручное управление, как в C, но с явностью и инструментами, которые ловят большинство ошибок.

C: malloc/free и тишина

В C вы зовёте malloc и free вручную. Компилятор не следит за временем жизни: забытый free — утечка, лишний free — двойное освобождение, free + использование — висячий указатель. Эти ошибки молчат до краха в продакшене. C полностью доверяет программисту и ничем не помогает.

Rust: borrow checker доказывает безопасность

Rust идёт в другую крайность: его borrow checker на этапе компиляции доказывает, что висячих указателей и гонок данных нет. Это мощно, но требует переучиться: концепции владения, заимствования и времён жизни добавляют когнитивную нагрузку, а некоторые корректные программы borrow checker отвергает, требуя обходных приёмов.

Zig: явность плюс инструменты

// Zig: владение видно глазами через явный аллокатор и defer
fn process(alloc: std.mem.Allocator) !void {
    const data = try alloc.alloc(u8, 256);
    defer alloc.free(data);  // освобождение рядом с выделением
    // borrow checker'а нет — за корректность отвечаете вы,
    // но GPA поймает утечку, а safe-сборка — выход за границы
}

Zig не доказывает безопасность памяти, как Rust. Зато он делает её обозримой: аллокатор явный, освобождение пишется рядом через defer, отладочный аллокатор ловит утечки, а safe-сборка ловит выход за границы и use-after-free в режиме отладки. Вы отвечаете за корректность сами, но язык даёт мощную страховочную сетку и не заставляет переучивать мышление.

Как работает под капотом

Отсутствие borrow checker означает, что компилятор Zig не строит граф времён жизни и не отвергает программы из-за заимствований. Безопасность достигается рантайм-проверками в safe-сборках (границы срезов, переполнения) и инструментами вроде GPA. В ReleaseFast проверки снимаются — как в C, ради максимальной скорости. Это сознательный выбор: простота языка в обмен на ручную ответственность.

Частые ошибки

Первая — ждать от Zig гарантий Rust: borrow checker'а здесь нет, висячий указатель возможен. Вторая — отлаживать память в ReleaseFast, где защита отключена; ищите ошибки в safe-режиме. Третья — считать Zig «небезопасным как C»: инструменты и явность ловят куда больше, чем голый C.

Итог

  • C: ручная память без страховки — молчаливые утечки и висячие указатели.
  • Rust: borrow checker доказывает безопасность ценой сложности и переучивания.
  • Zig: ручное управление, но явное и инструментированное — золотая середина.
  • В Zig за корректность отвечаете вы, но GPA и safe-сборка дают сильную страховку.
Проверьте себя
1. Есть ли в Zig borrow checker как в Rust?
AДа, идентичный
BНет; безопасность достигается явностью, defer и рантайм-проверками в safe-сборке
CДа, но только для срезов
DBorrow checker включается флагом
2. Где Zig находит большинство ошибок памяти, в отличие от молчаливого C?
AТолько в продакшене
BВ safe-сборке (границы, переполнения) и через отладочный аллокатор GPA (утечки)
CНигде, как и C
DЧерез borrow checker