Hacker News new | ask | show | jobs
by quantummagic 310 days ago
The idiomatic way in Zig is to return the simple unadorned error, but return detailed error data through a pointer argument passed into the function, allowing the function to fill in extra information before returning an error.

    const MyError = error{ FileNotFound, PermissionDenied };

    fn readFile(path: []const u8, outErrInfo: *ErrorInfo) ![]const u8 {
      if (fileMissing) {
        if (outErrInfo) |info| {
            info.* = ErrorInfo{
                .code = MyError.FileNotFound,
                .message = "File missing",
                .line = @line(),
            };
        }
        return MyError.FileNotFound;
      }
      return data; // success
    }
The advantage of this is that everything is explicit, and it is up to the caller to arrange memory usage for error data; ie. the compiler does not trigger any implicit memory allocation to accommodate error returns. This is a fundamental element of Zig's design, that there are no hidden or implicit memory allocations
4 comments

> The advantage of this is that everything is explicit, and it is up to the caller to arrange memory usage for error data

Likewise, this has the disadvantage that the caller must allocate space for the error payload, even if the error is very unlikely

Well, it is an optional parameter (my typo made that unclear), so the caller may omit it if desired. The reason for this particular tradeoff, is in favour of correctness rather than convenience, eg. if the failure is in an OOM condition, it can still be reported.
Ok this sounds nice. You would just need some syntax sugar to make this ergonomic (both in the side of function calling, and also in the side of the erroring function) but the fundamentals seem solid

I think there's space for a post-Rust, post-Zig language to combine the approaches of both and make it possible to do away with automatic heap allocation (when needed - not every piece of code wants to bother with this), but also don't make code overly verbose when doing so.

IMO error objects should’ve included a pointer to some extra preallocated memory.

In my C code I always allocate my error objects first, with usually 1024 bytes just for error strings.

In cases where i don’t care for error strings, i allocate 0 bytes for them.

I have a simple function to append error strings, and it checks for space. So all the code is ambivalent about whether this extra space exists.

works wonderfully.

should be ?*ErrorInfo in the header there =D
Thanks. There are a few thinko/typoes in the example (which can no longer be edited), but the basic outline survived okay.
So… pretty much how C does it.
Zig is basically the safety of Modula-2, with a revamped C like syntax, which is why it feels too little for a 2025 language.

Naturally the comptime part is new, but I wouldn't pick a language only because of that.

Well, to look at it optimistically, it's meant as a foundational language, to take over the role C still has today, of being the only true glue language that can underlie all the others. If Zig can actually take over that function, then it will be a major upgrade to the ecosystem, even though it will never be the language of choice for most projects.
Despite all my bashing regarding C, I would rather keep C around with a standard -fhardening, -fsafe, or whatever, with similar constraints, enabling enums without implicit numeric conversions, standard library types for arrays and strings, no decays of arrays into pointer.

Probably more than enough if we leave C as a kind of portable Assembler role, to be used as much as Assembly is, and leave everything else to safer managed languages.

A model just like UNIX authors themselves have applied when creating Inferno with Limbo, with the learnings of UNIX and Plan 9.

Not a fan of @, !, .{ } all over the place, or the struct based imports that look like Javascript require() instead of a proper module system.

The main difference is that C doesn't have error (result types) baked into the language. So the expectation would be in the Zig example above, the calling function would never even bother to inspect the error details, unless the error path was triggered by the called function.
Culture and coding standards count for a lot. C _can_ do this, but it's not normal to.

If Zig can foster a culture of handling errors this way, it'll be the way the community writ large handle errors.

It's still complete dogshit not to be able to have data there. Odin is much better here, iirc