11 ms·
In my opinion the biggest issue of Zig is that it doesn't allow attaching data to error. The error can only be passed via side channel, which is inconvenient an
by qouteall 11mo ago
In my opinion the biggest issue of Zig is that it doesn't allow attaching data to error. The error can only be passed via side channel, which is inconvenient and ENOURAGES TOOL DEVELOPERS TO NOT PASS ERROR DATA, which greatly increase debugging difficulty.
Somethings there are 100 things that possibly go wrong. With error data you can easily know which exact thing is wrong. But with error code you just know "something is wrong, don't know which exactly".
See: https://github.com/ziglang/zig/issues/2647#issuecomment-1444790576 https://github.com/ziglang/zig/issues/2647#issuecomment-1444...
> I just spent way longer than I should have to debugging an issue of my project's build not working on Windows given that all I had to work with from the zig compiler was an error: AccessDenied and the build command that failed. When I finally gave up and switched to rewriting and then debugging things through Node the error that it returned was EBUSY and the specific path in question that Windows considered to be busy, which made the problem actually tractable ... I think the fact that even the compiler can't consistently implement this pattern points to it perhaps being too manual/tedious/unergonomic/difficult to expect the Zig ecosystem at large to do the same
- qouteall 11mo agoI know that Zig doesn't allow attaching data to error for valid reasons. If error data contains interior pointer then it can easily cause memory safety problem. Zig doesn't have a borrow cheker or ownership system to prevent that. https://github.com/ziglang/zig/issues/2647#issuecomment-2670609338 https://github.com/ziglang/zig/issues/2647#issuecomment-2670...
- metaltyphoon 11mo ago> I know that Zig doesn't allow attaching data to error for good reasons. If error data contains interior pointer then it causes memory safety problem IMO this is not a good reason at all.
- 59nadir 11mo agoIf you wanted to have a parameter that gets filled in when there is an error, this exact issue will remain, it's completely unrelated to which language construct you use to capture errors and has more to do with having a good idea of how your errors are allocated, if they require allocation. I don't think the commenter in the GitHub issue thought this through at all, and probably didn't expect to have it be held up as some example of why you can't return tagged unions (because it's not an example of that, not even remotely).
- jandrewrogers 11mo agoThe "correct" way is highly context dependent with the added proviso that Zig assumes a low-level systems context. In this context, adding data to an error may be expedient but 1) it has a non-trivial overhead on average and 2) may be inadvisable in some circumstances due to system state. I haven't written any systems in Zig yet but in low-level high-performance C++20 code bases we basically do the same thing when it comes to error handling. The conditional late binding of error context lets you choose when and where to do it when it makes sense and is likely to be safe. A fundamental caveat of systems languages is that expediency takes a back seat to precision, performance, and determinism. That's the nature of the thing.
- qouteall 11mo agoIf the error rarely happens then passing error data shouldn't affect performance in visible way. If the error occurs in common path then it's designed wrongly. I agree that in special states like OOM passing error data with allocation is not ok.
- 59nadir 11mo agoError data being returned instead of just error codes doesn't require allocation at all, and never would, unless the specific unions that you're returning require as much. Zig already has tagged unions with a tag field and associated payload, that is exactly what you would return. The overhead isn't remarkably worse than the cost of modifying the value someone passed in to "Fill this in in case of errors" (which is what you have to do now in Zig).
- qouteall 11mo agoFor example , if error data contains string, if it directly point to input string it won't require allocation, but will have memory safety issue if error is passed to outer scope where input string is deallocated. Many errors copy string to make it usable in outer scope.
- librasteve 11mo agoFor quite a long time, I have been wondering why I like to code in Raku so much … in a round about way you set me thinking. Perhaps it’s because, in Raku, precision, performance and determinism take a back seat to expediency. (Sorry for the tangent).
- ajross 11mo agoThis seems kinda contrived. In practice that "ERROR DATA" tends not to exist. Unexpected errors almost never originate within the code in question. In basically all cases that "ERROR DATA" is just recapitulating the result of a system call, and the OS doesn't have any data to pass. And even if it did, interpreting the error generally doesn't every work with a microscope over attached data. You got an error from a write. What does the data contain? The file descriptor? Not great, since you really want to know the path to the file. But even then, it turns out it doesn't really matter because what really happened was the storage filled up due to a misbehaving process somewhere else. "Error data" is one of those conceits that sounds like a good idea but in practice is mostly just busy work. Architect your systems to fail gracefully, don't fool yourself into pretending you can "handle" errors in clever ways.
- TylerE 11mo agoSeralizing error data to text and then dumping that in a log can be pretty useful.
- skybrian 11mo agoI think you've skipped over all the cases where knowing the filename is actually helpful? It's true that sometimes it isn't. Also, a line number is often helpful, which is why compilers include it. Some JSON parsers omit that, which is annoying.
- ajross 11mo ago> Also, a line number is often helpful That's not error data, that's (one level of) a stack trace. And you can do that in zig, but not by putting call stack data into error return codes. The conflation between exception handling and error flagging (something that C++ did largely as a mistake, and that has been embraced by managed runtimes like Python or Java) is actually precisely what this feature is designed to untangle. Exception support actually turns out to have very non-trivial impact on the generated code, and there's a reason why languages like Rust and Zig don't include them.
- 11mo ago
- b33j0r 11mo agoThis is annoying. It’s because errors were designed to be a bitset and not have pointers. I would also prefer that they were a `union(enum)`. We are free to do that as a return type like `Result(T)` and just forgo using `try`, but yeah, I wish this was in there.
- cornstalks 11mo agoPeople are working on this. std.zon is generally considered to be a good example of how to handle errors and diagnostics, though it's an area of active exploration. The plan is to eventually collect all the good patterns people have come up with and (1) publish them in a collection, and (2) update std to actually use them.
- throwaway2037 11mo ago> how to handle errors and diagnostics, though it's an area of active exploration I am flabbergasted and exasperated by this sentiment. Zig is over 9 years old at this point. This feels this same kind of circular arguments from Golang "defenders" about generics and error handling.
- dlisboa 11mo agoGo gets a lot of flack for getting some things wrong but it was a stable and productive language within a couple of years. If you look at the current Zig website the hello world example doesn’t compile because they changed the IO interface. Something as simple as writing to the console. It’s easier to get things right if you have no issues breaking backward compatibility for a decade. It feels it’ll be well over 10 years before Zig is “1.0”.
- sneakinsnake 11mo ago+1
- sgt 11mo agoWhen will we see Zig 1.0?
- MrJohz 11mo agoInterestingly, I just read an article from matklad (who works a lot with Zig) talking about the benefits of splitting up error codes and error diagnostics, and the pattern of using a diagnostic sync to provide human-readable diagnostic information: https://matklad.github.io/2025/11/06/error-codes-for-control-flow.html https://matklad.github.io/2025/11/06/error-codes-for-control... Honestly I was quite convinced by that, because it kind of matches my own experiences that, even when using complex `Error` objects in languages with exceptions, it's still often useful to create a separate diagnostics channel to feed information back to the user. Even for application errors for servers and things, that diagnostics channel is often just logging information out when it happens, then returning an error.
- bragr 11mo agoYour and GP's two statements are not mutually exclusive. This paradigm can have significant benefits, and at the same time be too cumbersome for people to want to use consistently.
- qouteall 11mo agoI agree that building a special diagnostic system is better than just using language's builtin error system. However that takes efforts. Library developers tend to choose the path of least resistance, which is to not pass diagnostic information. The most convenient diagonistic system is the good old logging. Logging is easy. Maybe logging will be the de facto solution of passing error data in Zig ecosystem, due to psychological reasons.
- Xss3 11mo agoI tend to follow the rest of the ecosystem when developing libraries. If i wanted to make a zig lib id look at what other major libs are doing (or not doing) and copy that. If i found no consistency id be making a post like OP but from a different perspective.
- lenkite 11mo agoThe separation of error codes and diagnostics is fine, but the language needs a standard mechanism to optionally pass this error diagnostic information. Otherwise, everyone will develop their own different way with ZERO consistency and many will simply not pass error diagnostics at all.
- pyrolistical 11mo agoI can see pros and cons. Preventing data being attached to an error forces more clear and precise errors. Whereas lazy devs could just attach all possible data in a giant generic error if they don’t want to think about it.
- dns_snek 11mo agoI don't follow, because there's a possibility that someone somewhere might create a bad overly-generic error set if they were allowed to stuff details in the payload when those should be reflected in the error "type", it's a good idea to make the vast majority of error reporting bad and overly-generic by eliminating error payloads entirely?
- Galanwe 11mo ago> Preventing data being attached to an error forces more clear and precise errors. Okay maybe theorically, but in the real world I would like to have the filename on a "file not found", an address on a "connection timeout", a retry count on a "too many failures", etc.
- audunw 11mo agoBut also in the real world I may not be interested in any error information for the library I’m using. I’d like to be able to pass a null for the error information structure and have the compile optimize away everything related to tracking and storing error information. I’d like my parser library to be able to give me the exact file, line and column number an error occurred. But I’d also like to use the library in a “just give me an error if something failed, I don’t really care why” mode.
- Xss3 11mo agoId rather have data in a generic error type than no data in a specific error type. How useful is a file not found error type without data (the filename) when the program is looking for 50 files? Not very. How useful is a generic error type with '{filename} not found' as generic string data packed in? Quite.
- fpoling 11mo agoThere are plans in Zig to allow to include custom information into error stack trace https://github.com/ziglang/zig/issues/14446 https://github.com/ziglang/zig/issues/14446. But that is not implemented. In any case, when debugging annotating error with extra context often is not enough. One often needs a detailed trace of what happens before. So what I would like to see in any programming language is ability to do a structured logging with extra context from the call stack (including asynchronous support in languages that have that) that has almost zero overhead when the log is not printed. Various languages and runtimes have some libraries that try to do that, but the usage is awkward and the performance overhead is not trivial.
- ulbu 11mo agoclosed as not planned :)
- ayende 11mo agoNot this is explicitly marked as Not Planned
- jandrewrogers 11mo agoI made a go of this using the stacktrace functionality built into C++23. The overhead and complexity it introduced made it not worth it, unfortunately. There may be a way to do this but it seems non-trivial in implementation.
- otabdeveloper4 11mo agoYeah, every single newbie programming language designer starts with a maximalist position of "exceptions are hard, just return an error code", and then end up inventing their own shitty, ad-hoc and malfeatured exception handling system. I want off this ride.
- oofbey 11mo agoGo seems to do alright without exceptions. It’s just a convention to return errors, which seems fine as a practice.
- dns_snek 11mo agoAgreed, this is probably my biggest ongoing issue with Zig. I really enjoy it overall but this is a really big sticking point. I find it really amusing that we have a language that has built its brand around "only one obvious way to do things", "reducing the amount one must remember", and passing allocators around so that callers can control the most suitable memory allocation strategy. And yet in this language we supposedly can't have error payloads because not every error reporting strategy is suitable for every environment due to memory constraints, so we must rely on every library implementing its own, yet slightly unique version of the diagnostic pattern that should really be codified as some sort of a language construct where the caller decides which allocator to use for error payloads (if any). Instead we must hope that library authors are experienced and curious enough to have gone out of their way to learn this pattern because it isn't mentioned in any official documentation and doesn't have any supporting language constructs and isn't standardized in any way. There must be an argument against this (rather obvious) observation but I'm not aware of it?
- smj-edison 11mo agoGenuine question, how would error set unioning work with payloads? error.WriterFailed (might be getting the exact name wrong) is returned from many different writers, whether writing to a statically allocated array, writing to a socket, or writing to a file. Each error would have a very different payload, so how would you disambiguate between the different payloads with a global error type? The way I see it is either you have error sets, or payloads, but not both.
- weebull 11mo agoI'm also wondering what payload people want. There's already an error handling trace (similar but different to a normal stack trace) that captures the how the error propagates up to the point it's being handled so shows you exactly where the initial point was.
- deleted 11mo ago[deleted]
- tialaramex 11mo agoI agree, I like e.g. https://doc.rust-lang.org/std/string/struct.FromUtf8Error.html https://doc.rust-lang.org/std/string/struct.FromUtf8Error.ht... See, we were trying to make this data we had into a string, Rust says all strings are UTF-8 encoded - but, turns out the data wasn't UTF-8 after all, here's an error with the data inside it. Or a really delicate piece of design, (nightly for now) Vec::push_within_capacity. We're hoping the growable array (Vec<T>) has enough space for this T, thus getting rid of it, but if not we don't want to grow the array, maybe we're bare metal software and can't afford to allocate on this hot path, so we get back an error with the T we were trying to push onto the array inside it, so we can do something else with that T but only when the problem happened, otherwise it's gone.