openlang

Unwrapping a C pointer with .? is an assertion

.? on a pointer that came back from C is an assertion, not a check. It says null is impossible here. In a safety-checked build a null panics, and in ReleaseFast or ReleaseSmall it is undefined behaviour. For malloc that claim is false by definition.

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

The second line returns the failure to the caller, which is the honest version. Functions returning [*c]u8 are worse, because the type has no ? to remind anyone. Coercing a C pointer to a non-optional pointer is the same assertion with no syntax at all. The Zig documentation calls a zero address there safety-checked illegal behaviour.

So in wrappers I read, every C pointer goes through an optional first, and orelse names the error. What I have not decided is which error. error.OutOfMemory is right for malloc and wrong for many functions that return null.

后续讨论 5

顺着彼此的依据读下去
  1. 固定链接

    Nullness isn't the only thing that pointer drops. The length goes too. Once the wrapper has turned the result into a [*]u8, it's the last place that knows n. I'd return p[0..n] as a slice, so the caller gets the address and the length together and bounds checks have something to work with.

    The return type also can't say who frees it, so the doc comment has to. Memory from c.malloc goes back through c.free, not through whatever allocator the caller usually passes around. If the wrapper doesn't need malloc specifically, allocating through std.heap.c_allocator keeps that pairing inside Zig's own allocator interface.

  2. 固定链接

    On which error to name, the information is usually in errno, and the one rule that never changes is to read it right after the call, before anything else can run. Logging, a formatting call, really any library function is allowed to change it even when nothing failed. Python's ctypes has the same trap, which is why it makes you ask for use_errno=True and then read ctypes.get_errno() instead of the raw variable.

    So the step order I'd write down is call, check for null, copy errno into a local, then decide which error it maps to. The mapping can be a switch over a few known values with a catch-all for the rest, and that last branch is where most of the judgment goes.

  3. 固定链接

    Agreed on the slice. The wrapper returns []u8 now, and its doc comment names c.free as the release, with a sentence for the error path too, where nothing is returned and nothing needs freeing.

    I'm keeping malloc itself, because the buffer is handed to a C function that may call realloc on it later. That is a transfer to C, and once it happens the slice I returned stops being valid as well, which the doc comment now says.

  4. 固定链接

    Look at what that catch-all branch can return, though. A Zig error is just a name. It carries no payload, so else => error.Unexpected drops the errno value that told you what actually happened. If the value matters later, it has to travel some other way, an out parameter or a diagnostics struct the caller passes in, and the error name only says which kind of thing went wrong.

    I'd keep the known branches as small as possible, one errno to one name each, so a reader can check each case against the C man page without reading the others.

  5. 固定链接

    Close to TypeScript's !, which compiles to nothing, except that Zig's .? panics in a safety-checked build. Under ReleaseFast that check is gone, so the one you leaned on in testing isn't in the build you ship.

同一个问题的其他角度

  1. What the canary check looks like at -O2

    Three small C functions, gcc -O2 -fstack-protector-strong -S -masm=intel, x86-64 Linux. read_line and read_count get the same…

    回复 1最近活动 assemblyc
  2. Callbacks from C lose the arena I set up

    A C library calls back into my Odin code, and the callback can't see the context I set up before registering it. A proc "c" g…

    回复 8最近活动 odinc
  3. Clearing one status flag with |= cleared all of them

    |= on a write-1-to-clear status register clears more than the bit you named. The overrun flag clears by writing 1 to it. Writ…

    回复 5最近活动 c