Btw, not that it matters much, but this
could be written like this:
const data: [1024]u32 = @splat(1);
Otherwise, I like this idea! ![]()
Btw, not that it matters much, but this
could be written like this:
const data: [1024]u32 = @splat(1);
Otherwise, I like this idea! ![]()
When doing @splat for large arrays, I always encourage to look at the disassembly so that you aren’t accidentally bloating your binary.
This is bit poor example, but showcases what can happen:
https://zig.godbolt.org/z/dr6GEqaEG
For temporary arrays or const arrays, splat often gets optimized away.
The ability to see the zig code equivelant to what comptime operations generate.
Yeah I know completely forgot ahah, but yeah I think some form of syntax support for basically automatic windowing, could be very handy
Huh, I would have expected that to generate the exact same assembly! Looks like a bug to me. Good to know in any case!
Alright, I’ve got my wish (after having hanged around in this thread for some time without making one): I wish for this to be fixed ![]()
…about C vs Zig designated init…
What’s missing in zig for that? (I admit I did not look at the standard, but zig already does many struct init that C does).
The main feature I’m missing is more flexible array initialization (direct indexing and allowing incomplete array initialization which would need to be filled up with the declaration-defaults).
The next thing which is often coming in very handy is chained designators (e.g. x.y.y = 123).
E.g. have a look at this C code:
state.pip = sg_make_pipeline(&(sg_pipeline_desc){
.layout = {
.attrs = {
[ATTR_texcube_pos].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_texcube_color0].format = SG_VERTEXFORMAT_UBYTE4N,
[ATTR_texcube_texcoord0].format = SG_VERTEXFORMAT_SHORT2N
}
},
.shader = shd,
.index_type = SG_INDEXTYPE_UINT16,
.cull_mode = SG_CULLMODE_BACK,
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.label = "texcube-pipeline"
});
…the attrs initialization has all the missing features:
[ATTR_texcube_pos].format = SG_VERTEXFORMAT_FLOAT3)The current workaround in Zig is to use an init-block, but I feel like this is a really awkward hack for such simple situations. Init-blocks should only be required when the initialization actually requires complex initialization logic.
E.g. this is the same code in Zig (but this is only okay-ish because the attrs array is wrapped in another public struct):
state.pip = sg.makePipeline(.{
.shader = sg.makeShader(shd.texcubeShaderDesc(sg.queryBackend())),
.layout = init: {
var l = sg.VertexLayoutState{};
l.attrs[shd.ATTR_texcube_pos].format = .FLOAT3;
l.attrs[shd.ATTR_texcube_color0].format = .UBYTE4N;
l.attrs[shd.ATTR_texcube_texcoord0].format = .SHORT2N;
break :init l;
},
.index_type = .UINT16,
.depth = .{
.compare = .LESS_EQUAL,
.write_enabled = true,
},
.cull_mode = .BACK,
});
To be fair to Zig, only very few languages come close to C99 designated-init.
How about this?
for (@IntRange(0,31)) |i| {} // Ranges should be closed intervals, fight me
That’s just a type, so for (u8) would give you [0,255]. Meaning you could write this particular one for (u5), but options like for (@IntRange(0xc0, 0xff)) would exist as well.
I don’t think the original 0..n+1 syntax should narrow to an @IntRange though. Tempting as it is from a correctness standpoint, I bet ranged integers will be different enough to work with that there will be times when “make it unsigned and make it Large” is what’s wanted.
Working (draft) spec, so that we know what’s formally a Zig program.
I know a spec will a lot of constraint to the evolution of the language, but the constraint could also be a restraining factor to adding the “next shiny new thing in town” to the language.
It’ll also enable others to start writing alternative implementation of Zig.
But if they’re half-open you get nice properties:
@IntRange(a, b).len == b - a
// and therefore
@IntRange(0, n).len == 0
// adjacent intervals look contiguous:
@IntRange(a, b) ++ @IntRange(b, c) == @IntRange(a, c)
// No need to special case the empty interval
@IntRange(0, 0)
// How would you even right that for a closed interval?
@ClosedIntRange(0, -1)
// Or for unsigned?
@ClosedIntRange(@as(u32, 0), @as(u32, -1)) // uh oh, looks like we're doing 2^32 iterations
Related essay by Dijkstra on why numbering should start at zero.
You’re treating these as though they are values, but they’re types.
At least consider that I might have read Dijkstra once or twice, quite possibly before you were born. I have considered and rejected all of those arguments a long, long time ago, as did the Ada authors when they actually implemented ranged integers.
I’ll save my ammo for some other time, however. Doesn’t seem like the place for it.
But you might figure it out on your own! So I encourage you to ask: “if someone who already knew all that in his bones, and takes it for granted, still thinks ranged integer types should be closed intervals: why might that be?”
Related question: why does Zig use closed ranges in switch statements?
I’m not engaging with that level of condescension.
It would be nice if the Matrix stuff saw some love I did a small building a neural network course that was python based and I just the equivalent with zig. It just seems like every time I start using multiple dimensional arrays it feels clumsy in zig. I think this will go away with continued video game optimizations stuff.
The ability to extend existing functionality. Like instead of creating a wrapper for an std struct and adding your 2 custom methods you could just extend the existing flow. Like std.ArrayList.mycustom_methond.
This is already effectively possible, though not with the syntax you’re proposing:
const std = @import("std");
const E = enum {
foo,
bar,
fn T(e: E) type {
return switch (e) {
.foo => []const u8,
.bar => [32]u8,
};
}
};
fn useUnionThingy(comptime e: E, u: e.T()) void {
switch (e) {
.foo => std.debug.print("Hello {s}!\n", .{u}),
.bar => @compileError("Sema doesn't hit this"),
}
}
pub fn main() void {
const e: E = .foo;
const u: e.T() = "Ziggit";
useUnionThingy(e, u);
}
If you’re also using the union type without a comptime known field, you could define E and U with either:
const U = union(enum) {...} and const E = @typeInfo(U).@"union".tag_type.?const E = enum {...} and const U = union(E) {...}Then use @FieldType(U, @tagName(e)).
There’s lots of subtle variations on this that depend on exactly what you’re trying to do, but I think this gives the overall idea.
The last reply gave a nice code example.
At some point, I think the language should add built-in support for garbage-collected strings ala Delphi. Given the importance of language to human existence, having a specialized type is well justified in my opinion. Since strings do not contain pointers, we’re looking at a fairly simple reference counting system.
A testament to Andrew and the core team, I’m having a hard time coming up with something. I could not say the same if we were talking about other languages.
I think my wish would then be, to magically get the state of the git repository from the future when zig hits 1.0. And don’t try to trick me genie, that also includes the bootstrap binaries commited to source control. I’d be quite disappointed to have the code without the ability to compile it.
True I had to think long and honestly at this point it feels more like asking for my money back when I already have the sandwich
Most features in Zig are good. I’m productive with the language. One ergonomic improvement would be adding support for returning value from a multiple statement block. E.g.
const x = if (condition) {
s1;
s2;
break_with s3;
} else {
s1;
s2;
or_last_statement_as_the_value_of_the_block
}
Same with orelse, catch, and switch.
I really don’t like the current work-around of using label. I always have to look up the label syntax and their placement. It just breaks the flow of programming.
Yeah. That’s the pattern I’ve started using myself. The only slightly annoying thing is that the tag and the object are now two separate things that I have to “carry around” in the code
To be fair it is not a huge problem.
Ah yes another thing I would love is a @noAlias built-in.
It accepts a pointer a and returns another pointer b.
The semantics are that every load and store through b are isolated from the rest, and the modification get visible to the rest of the pointers only when b falls out of scope.
The usage would be:
const row1 = @noAlias(matrix.row(1));
const row2 = @noAlias(matrix.row(2));
const row3 = @noAlias(matrix.row(3));
for (row1, row2, &row3) |v1, v2, *v3| {
// Here the optimizer should have all the information that it needs
v3.* = v1 + v2;
}