-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
In/OutStream design flaw introduced with new error set changes. #764
Comments
Code doesn't compile, as we now have this issue: ziglang/zig#764
Thanks for the early adoption. Let's get this sorted out.
This aspect should be fine. This isn't c++ where we duplicate all the work in every file and then rely on the linker to toss out redundant definitions. LLVM actually has an optimization pass that merges compatible function definitions together. We could potentially even notice when the difference between instantiations differs only in error sets and save time and space even in debug mode. The API ergonomics and semantics on the other hand, let's talk about that. Here are our options:
Sorry about all the breaking changes in a small amount of time. I've been trying to do the breaking changes all together and sooner to avoid even more pain later. |
Actually, the code explosion is more a compile speed concern. Anyways, I think the ergonomics aspect is the bigger concern. I don't know the exact solution to this either, which is why I didn't propose anything. Something similar to Maybe #470 is what would solve all of this? |
Came across this a few weeks ago when doing a deflate implementation. https://github.com/tiehuis/zig-deflate/blob/master/deflate.zig#L99 This particular case was a bit more troublesome than simply taking a var however since I needed to also generate a wrapped type and store in the new case. Couldn't think of a way at the time to get the comptime |
Relevant #764 dwarf debug info is modified to use this instead of std.os.File directly to make it easier for bare metal projects to take advantage of debug info parsing
I was all set to begin overhauling std with comptime interfaces (#1829) when it occurred to me that, of all the interfaces I know about in std, only InStream and OutStream have any real reason to be comptime, by virtue of being the only ones that return dynamic error sets. The ergonomics of both creating and using interfaces could be a lot cleaner if we didn't have to deal with that, so I'd like to try and gather some more suggestions and opinions on how we could handle errors with InStream and OutStream. One thing that was not suggested was to have functions that actually care about the error set information take it as a parameter along with the stream interface: |
I think that's worth trying. Wouldn't it look like this?
I got bit by this problem recently as well: Lines 1092 to 1093 in 0afb868
You can see I gave up and used Then there's still code bloat to solve - this is a really gnarly problem. I certainly agree with the title of this issue that this is a design flaw. |
That's a way of doing it. I was thinking that I suppose I'll experiment and see which is more of a headache to rewrite std with. |
I just realized that @Hejsil already wrote all this up in the original description back in February. |
So something I've been asking myself is, why do we even care about the ErrorSet in the context of interfaces? The function taking an interface for generic purposes can't possibly cover all possible error cases, so would always end up punting on the ones it didn't know about when it was written anyway. Presumably someone up the call stack might care about handling all the possible errors, but they would already have the ErrorSet since they have the implementation, right? They'd just have to cast to it. So I guess what I'm saying is, what is the real problem if all interfaces were to return |
That might be a worthwhile tradeoff, given the downsides of status quo. But to answer your question the real problem is that you can no longer switch on the error, and have the compiler make sure that you handled all the possible errors, even when the code is later changed so that more error codes are possible, and similarly the compiler would no longer detect dead code for handling an impossible error code. To summarize, it makes it more difficult to write code where it's clear that you've handled all the possibilities. You'll probably have to do a |
Another option. Have interfaces with custom errors have a const E = error{A, B};
const stream = &OutStream(E).stream;
// Validates that the errorset passed in is a superset.
const stream2 = stream.castToErrorSuperSet(error{A,B,C}); |
I think with the interface pattern I'm trying right now this won't be a big deal. Most code will want to take the comptime interface (retrieved as |
The main goal here is to make the function pointers comptime, so that we don't have to do the crazy stuff with async function frames. Since InStream, OutStream, and SeekableStream are already generic across error sets, it's not really worse to make them generic across the vtable as well. See #764 for the open issue acknowledging that using generics for these abstractions is a design flaw. See #130 for the efforts to make these abstractions non-generic. This commit also changes the OutStream API so that `write` returns number of bytes written, and `writeAll` is the one that loops until the whole buffer is written.
I think this can be considered solved by #17344. Any further discussion is welcome. |
So, before error sets, we had In/OutStreams that could be implemented on a memory buffer, files or whatever would fit this interface, and could function to take In/OutStreams, and it would work on all of these.
Now, with error sets, I can see that In/Outstream was redesigned to be generic, taking an error set as the parameter. This ruins the useability of the In/OutStream:
Now, you would have to pass the error type to all functions taking an In/OutStream, for it to work again:
If this is the way they should now be used, then can I be sure that
foo
doesn't explode into a million instances offoo
, as different OutStreams are used? Users of my function now also have to call it like this:Which, idk, seems not so ergonomic for the users of my library. It relies on the fact, that the implementer of the OutStream adaptor was kind enough to actually provide this
Error
constant field. If the implementer did not do this, the user is more screwed, and would have to write this code:Ooh god, plz no.
The text was updated successfully, but these errors were encountered: