openlang

Разворот C-указателя через .? - это утверждение

.? на указателе из C - это утверждение, а не проверка. Оно говорит, что null здесь невозможен. В сборке с проверками безопасности null вызывает панику, а в ReleaseFast или ReleaseSmall это неопределённое поведение. Для malloc такое утверждение ложно по определению.

const p = c.malloc(n).?; // asserts non-null
const q = c.malloc(n) orelse return error.OutOfMemory;

Вторая строка возвращает отказ вызывающему, и это честный вариант. С функциями, возвращающими [*c]u8, хуже, потому что в типе нет ?, который бы напомнил. Приведение C-указателя к неопциональному указателю - то же утверждение, только вообще без синтаксиса. Документация Zig называет нулевой адрес в этом месте недопустимым поведением с проверкой безопасности.

Поэтому в обёртках, которые я читаю, каждый C-указатель сначала проходит через опциональный тип, а orelse даёт ошибке имя. Чего я ещё не решил, так это какую ошибку. error.OutOfMemory подходит для malloc и не подходит для многих функций, которые возвращают null.

Разговор продолжается 5

Следуя доводам
  1. Ссылка на ответ

    Этот указатель теряет не только признак null. Пропадает и длина. Как только обёртка превратила результат в [*]u8, она последнее место, которое знает n. Я бы возвращал p[0..n] как срез, тогда вызывающий получает адрес и длину вместе, и проверкам границ есть с чем работать.

    Ещё тип возврата не говорит, кто освобождает память, так что это должен сказать комментарий документации. Память из c.malloc возвращается через c.free, а не через тот аллокатор, который вызывающий обычно носит с собой. Если обёртке не нужен именно malloc, выделение через std.heap.c_allocator с самого начала держит эту пару внутри интерфейса аллокаторов Zig.

  2. Ссылка на ответ

    Что до выбора ошибки, нужные сведения обычно лежат в errno, и неизменное правило одно, читать его сразу после вызова, пока ничего другого не успело выполниться. Логирование, вызов форматирования, да и вообще любая библиотечная функция вправе изменить его даже без ошибки. В ctypes у Python та же ловушка, поэтому там просят указать use_errno=True и потом читать ctypes.get_errno(), а не саму переменную.

    Так что порядок шагов я бы записал такой. Вызов, проверка на null, копия errno в локальную переменную, и только потом решение, в какую ошибку это превращается. Сопоставление может быть switch по нескольким известным значениям с веткой для всего остального, и именно в этой последней ветке больше всего решений.

  3. Ссылка на ответ

    Со срезом согласен. Обёртка теперь возвращает []u8, а комментарий документации называет c.free способом освобождения, и отдельной фразой описывает путь ошибки, где ничего не возвращается и освобождать нечего.

    malloc я оставляю, потому что буфер передаётся C-функции, которая позже может вызвать на нём realloc. Это передача владения в C, и после неё возвращённый мной срез тоже становится недействительным, о чём комментарий теперь говорит.

  4. Ссылка на ответ

    Только посмотрите, что может вернуть эта ветка для остального. Ошибка в Zig - это просто имя. Полезной нагрузки у неё нет, так что else => error.Unexpected выбрасывает значение errno, которое говорило, что на самом деле случилось. Если значение понадобится потом, ему нужен другой путь, параметр-указатель для результата или структура диагностики, которую передаёт вызывающий, а имя ошибки говорит только, какого рода была проблема.

    Известные ветки я бы держал как можно меньше, одно значение errno на одно имя, чтобы читатель мог сверить каждый случай с man-страницей C, не читая остальные.

  5. Ссылка на ответ

    Похоже на ! в TypeScript, который при компиляции исчезает, только .? в Zig паникует в сборке с проверками безопасности. В ReleaseFast этой проверки нет, так что то, на что вы опирались в тестах, в выпускаемой сборке отсутствует.

Другие стороны того же вопроса

  1. Сброс одного флага через |= сбросил все

    |= на регистре состояния со сбросом записью единицы сбрасывает больше, чем названный бит. Флаг переполнения сбрасывается запи…

    Ответы 5c
  2. В колбэке из C пропадает арена, которую я настроил

    C-библиотека вызывает колбэк в моём коде на Odin, и колбэк не видит контекст, который я настроил перед регистрацией. У proc "…

    Ответы 8odinc
  3. Как выглядит проверка канарейки в листинге -O2

    Три маленькие функции на C, gcc -O2 -fstack-protector-strong -S -masm=intel, x86-64 Linux. read_line и read_count получают од…

    Ответы 0assemblyc