a long time has passed since 0.16, I didn’t really find that many compilation errors upgrading from 0.16 (except ‘++’ and ‘**’)
it doesn’t have to be accurate, just what comes to mind
a long time has passed since 0.16, I didn’t really find that many compilation errors upgrading from 0.16 (except ‘++’ and ‘**’)
it doesn’t have to be accurate, just what comes to mind
For me the main ones in the migration were:
void can’t be initialized with .{} or void{}, use {}// Before
const Food = extern union {
breakdown: struct {...},
arr: [8]f32,
vec: @Vector(8, f32),
};
// To add a.vec = b.vec + c.vec;
// After
const Food = extern union {
breakdown: struct {...},
arr: [8]f32,
pub fn vec(self: @This()) @Vector(8, f32) {
return self.arr;
}
}
// To add a.arr = b.vec() + c.vec();
// My best friend has returned!
fn bitCast(T: type, value: anytype) T {
const N = @sizeOf(T);
if (@sizeOf(@TypeOf(value)) != N) @compileError("Bitcast must have equal sizes");
var ans: T = undefined;
const ansBytes: *[N]u8 = @ptrCast(&ans);
ansBytes.* = @as(*const [N]u8, @ptrCast(&value)).*;
return ans;
}
// Example
const S = extern struct{
a: f32,
b: f32,
};
// To convert to [2]f32:
// const C = bitCast([2]f32, myVariable);
The main thing I’m aware of this release cycle is that the make and the configure steps of zig build are now separate processes. Also the elements of the OptimizeMode enum were renamed, which mostly only affects you if you were writing mode == .Debug.
I’m not an expert, but: the configure step (which includes running your build.zig logic) is compiled in debug mode (so safety gains and also if you’re on x86_64, you benefit from the speed of the self-hosted backend), while the maker is compiled in ReleaseSafe (I think? which is also now just safe maybe?), so you get some speed gains, the configure and make steps being separated is good for the possibility of further sandboxing arbitrary (build) code execution down the line, and more of the compiler can be shipped as source code, which helps binary and tarball size (since typically that source code was coming your way anyway) and i’m told also makes tinkering on the compiler a bit easier.
The groundwork for the compiler server, api to interact for 3rd party tooling, which should make zls (and others) much better.
(you won’t see improvement in this release, currently it only provides parity with existing ways to get info)
someday, insha’Allah, hopefully, I will ask how zig.build is executed, for some reason I always postpone asking the question
oh, thanks. did not expect things related to extern to ever change (since the C ABI is old and stable), I think I should give actually learn about it
isn’t the evented io being implemented, and the translate-c is supposed to work with gtk on .17? but the team working on the backend makes me feel happy, maybe because it means it’s getting more and more stable (getting closer to its last shape)
anyways, thanks for your responses, I was afraid I was missing something or using some deprecated features without checking the docs
Neither evented Io nor the backends have anything to do with what I was talking about.
I mean the new API to interact with the compiler for tools like zls, won’t provide a noticeable improvement to zls in 0.17, but future releases will enable zls, and other tools, to do more.
Other features may be noticeably improved.
Devlog is a good preview:
I love reading those, but I did not want to bother you guys from the Zig team with this question.
though if you could allow me to ask a favor from you man, can you someday -only on your free time and if you feel like it- tell us why adding Canceled to Io.Reader is objectively wrong, because from what I understand from your talks, I believed it was already included. so now I do feel like I’m missing on some very important information. thanks, and sorry for the bother (I hate bothering people, not shy)
Let me turn the question around for you: why not add error.OutOfMemory to Io.Reader?
because it does not belong to it… unexpectedly, that helped me understand.
so “it was not designed to have [responsibility for] this error in first place” 'k, got it
note: I feel silly because I only just read the contents of Io.Reader.Error. now the rest of this post seems pointless, anyways, thanks for the answer
Surely it does belong in some situations? Just like reader functions are infallible in some situations? Shouldn’t it be solved by some kind of metaprogramming to include the exact error set in the exact reader?
The reader/writer APIs used to be generic, they were a mess because of it.
The new APIs do support arbitrary errors, in the form of the vague error.ReadFailed/error.WriteFailed; it is then up to the implementation to provide something more useful through some other means, and this is fine in practice since you just don’t need to care about implementation-specific errors unless you have access to the implementation already (surely there is an exception, but I haven’t found it).
For error.OutOfMemory specifically, only (in std) Writer.Allocating would use it, and doesnt have any other error, so error.WriteFailed always means error.OutOfMemory, no need to do any special handling there.
They used to be generic, but they weren’t a mess because of that specifically. They were a mess because it wasn’t the right abstraction. See Andrew Kelley’s talk Don’t Forget To Flush if you’d like more details. Including the buffer in the interface (aka above the vtable) is a profound and highly beneficial change. The new implementation also allows a single instance of the machine code, which has performance benefits because of instruction caches.
I’m suggesting being generic only on the return error type. I’m not suggesting monomorphizing the entirety of the Reader (which would throw away those performance improvements). A cast of anyerror to the specific error set shouldn’t have to duplicate the machine code.
This sort of generalization has president in the standard library in at least once place I can point to: Queue and TypeErasedQueue. There are probably many other examples.
Sorry, that was an oversimplification; I am aware of the specifics, the generics were more of an issue of how zig does them, and because readers/writers get nested, resulting in hard to specify types, or overuse of anytype. And ofc, the api was just poorly designed.
My point was you’d be introducing back the pain of generics, that wasn’t the main issue, but it was a major pain point. Being more specific and scoped with generics does lessen the issue, but nested generics will always be a pain in zig.
Zig already has an error system that’s a joy to work with. Why does Reader introduce a worse error system? Usually friction in Zig is intentional. It’s to save the user from a pitfall they might not otherwise know about.
I don’t believe this friction is intentional. I also don’t believe there’s no way to remove this friction.
Suppose the return type is the only thing that’s generic about Io.Writer and that you are trying to write some data that gets compressed or not based on runtime information and then written to a file.
In status quo std, I believe there either is or was a deflate implementation (I haven’t used it). Assuming it is there, when you want to compress things, you can patch it into your Io.Writer chain and you’re good to focus on the logic of your program.
If the error return type is generic, suddenly it gets much harder to provide a plug-and-play deflate implementation in this way, because it matters where the deflate bit got its data from (because the possible errors it has to react to are different). And now you have to work with code paths of different types (literally) at runtime based on whether you’re compressing or not.
This is the pitfall you sound like you might not otherwise know about.
Can you explain why it’s much harder? To me it seems like the generic error set of the deflate Writer would need to be the union of the underlying Writer’s generic error set and any deflate specific errors (error.OutOfMemory?). Surely these are the exact errors the program needs to handle?
BTW, you can get GTK translating on 0.16.0 using my backported translate-c:
We use it in Ghostty and there have been no issues since it got merged late July. I’m updating the fork today!
sure, that’s a fair point! It sounds like you would like
pub fn Writer(comptime Error: type) type {
return struct {
// body is status quo except with Writer.Error replaced with Error?
};
}
I would not like this. For one thing, now we’re paying twice.
One time that we are paying is in binary size. Functions are monomorphized = reinstantiated and recompiled based on their comptime captures, so std.Io.Writer.writeAll now is compiled for every time Writer(E) is called with a different value of E.
And then to make matters worse, in order to provide something that is sort of like an interface, we either wind up having to have a VTable as well in order to let code outside of the standard library provide implementations of drain, or we return to the previous status quo and make Writer also depend at comptime on the functions that would be in its VTable.
Now imagine a situation where you have to store a Writer on your context struct—but again, precisely the type of the writer it is depends on runtime information. Now we’re back to wanting to reinvent the previous status quo of AnyWriter so that our program passes type-checks! A sad and sorry state of affairs indeed.
While we are at the topic, could I please request a review of my PR#31579?
It would be nice if that manages to land before 0.17 since it’s not really big.