9 ms·
Partially Matching Zig Enums
- dlahoda 1y agofn main() { if false { const _:() = panic!(); } } Fails to compile in Rust.
- Ygg2 1y agoWhy would it? If I recall correctly, const and static stuff basically gets inlined at the beginning of the program.
- Sharlin 1y agoSure, because it's compile-time code inside a (semantically) run-time check. In recent Rust versions you can do fn main() { const { if false { let _:() = panic!(); } } } which compiles as expected. (Note that if the binding were `const` instead of `let`, it'd still have failed to compile, because the semantics don't change.)
- tialaramex 1y agoPerhaps more succinctly: fn main() { const _:() = const { if false { panic!() } }; } It's fine that we want a constant, it's fine that this constant would, when being computed at compile time, panic if false was true, because it is not.
- dlahoda 1y agonot sure it is to be equivalent to zig. in zig they have one brach const. in rust example from you, whole control flow ix is const. which is not rquivalent to zig. so how to have non const branches?
- the__alchemist 1y agoI have no idea what that's trying to do. A demonstration that rust is a large language with different dialects! A terse statement with multiple things I don't understand: - Assigning a const conditionally? - Naming a const _ ? - () as a type? - Assigning a panic to a constant (or variable) ? To me it might as well be: fn main() { match let { if ()::<>unimplemented!() -> else; } }
- spiffyk 1y agoThis post shows how versatile Zig's comptime is not only in terms of expressing what to pre-compute before the program ever runs, but also for doing arbitrary compile time bug-checks like these. At least to me, the former is a really obvious use-case and I have no problem using that to my advantage like that. But I often seem to overlook the latter, even though it could prove really valuable.
- dwattttt 1y agoI love the idea, but something being "provable" in this way feels like relying on optimisations. If a dead code elimination pass didn't remove the 'comptime unreachable' statement, you'll now fail to compile (I expect?)
- anonymoushn 1y agoA lot of Zig relies on compilation being lazy in the same sort of way.
- dwattttt 1y agoFor the validity of the program? As in, a program will fail to compile (or compile but be incorrect) if an optimisation misbehaves? That sounds as bad as relying on undefined behaviour in C.
- Laremere 1y agoIt's not an optimization. What gets evaluated via the lazy evaluation is well defined. Control flow which has a value defined at comptime will only evaluate the path taken. In the op example, the block is evaluated twice, once for each enum value, and the inner switch is followed at comptime so only one prong is evaluated.
- anonymoushn 1y agoWell, for example you may have some functions which accept types and return types, which are not compatible with some input types, and indicate their incompatibility by raising an error so that compilation fails. If the program actually does not pass some type to such a function that leads to this sort of error, it would seem like a bug for the compiler to choose to evaluate that function with that argument anyway, in the same way that it would be a bug if I had said "template" throughout this comment. And it is not generally regarded as a deficiency in C++ that if the compiler suddenly chose to instantiate every template with every value or type, some of the resulting instantiations would not compile.
- judofyr 1y agoThis is one the reasons I find it so silly when people disregard Zig «because it’s just another memory unsafe language»: There’s plenty of innovation within Zig, especially related to comptime and metaprogramming. I really hope other languages are paying attention and steals some of these ideas. «inline else» is also very powerful tool to easily abstract away code with no runtime cost.
- Ygg2 1y ago> «inline else» is also very powerful tool to easily abstract away code with no runtime cost. Sure, but you lose the clarity of errors. The error wasn't in `comptime unreachable` but in `inline .a .b .c`.
- jeltz 1y agoI disagree, I would say the error is in "comptime unreachable" or maybe the whole "switch (ab)".
- Ygg2 1y ago`comptime unreachable` isn't the underlying cause and not the place you should fix in this case. A good error tells you what caused it, and where to fix it.
- binary132 1y agoAdding a new case is legitimate, failing to handle it (by reaching unreachable) is an error.
- diegocg 1y agoAs someone who uses D and has been doing things like what you see in the post for a long time, I wonder why other languages would put attention to these tricks and steal them when they have been completely ignoring them forever when done in D. Perhaps Zig will make these features more popular, but I'm skeptic.
- 1y ago
- veber-alex 1y agoI don't understand. Isn't this only useful if the value you match on is known at compile time?
- alpinisme 1y agoThe problem this is meant to solve is that sometimes a human thinking about the logic of the program can see it is impossible to reach some code (ie it is statically certain) but the language syntax and type system alone would not see the impossibility. So you can help the compiler along. It is not meant for asserting dynamic “unreachability” (which is more like an assertion than a proof).
- sekao 1y agoThe code example will work even if `u` is only known at runtime. That's because the inner switch is not matching on `u`, it's matching on `ab`, which is known at compile time due to the use of `inline`. That may be confusing, but basically `inline` is generating different code for the branches .a and .b, so in those cases the value of `ab` is known at compile time. So, the inner switch is running at compile time too. In the .a branch it just turns into a call to handle_a(), and in the .b branch it turns into a call to handle_b().
- 38 1y ago[dead]
- the__alchemist 1y agoI love how this opens with the acknowledgement we've made a mess of choice-like data structure terminology!
- uecker 1y agoIt is great to see other languages getting the same compile-time meta programming features as C ;-) https://godbolt.org/z/P1r49nTWo https://godbolt.org/z/P1r49nTWo
- deleted 1y ago[deleted]
- loeg 1y agoI didn't know godbolt let you include URLs. Fascinating: https://raw.githubusercontent.com/uecker/noplate/main/src/core.h https://raw.githubusercontent.com/uecker/noplate/main/src/co... I think Zig gets some points here for legibility ;-).
- uecker 1y agoNote that only one or two lines of this file is needed here, the rest is for other experiments... And a user would simply use the "literal" macro. I also do not think the Zig version is really that much clearer or obvious. I would not recommend to write such code anyway, neither in C nor in Zig. The "unreachable" version is far better.
- rvrb 1y agoI did not realize you could inline anything other than an `else` branch! This is a very cool use for that.
- agons 1y agoIs there a reason the Zig compiler can't perform type-narrowing for `u` within the `U::A(_) | U::B(_)` "guard", rendering just the set of 2 cases entirely necessary and sufficient (obviating the need for any of the solutions in the blog post)? I'm not familiar with Zig, but also ready to find out I'm not as familiar with type systems as I thought.
- rvrb 1y agoit can narrow the payload: https://zigbin.io/7cb79d https://zigbin.io/7cb79d I think the post would be more helpful if it had a concrete use case. let's say a contrived bytecode VM: dispatch: switch (instruction) { inline .load, .load0, .load1, .load2, .load3 => |_, tag| { const slot = switch (tag) { .load => self.read(u8), else => @intFromEnum(tag) - @intFromEnum(.load0), }; self.push(self.locals[slot]); continue :dispatch self.read(Instruction); }, // ... } "because comptime", this is effectively the same runtime performance as the common: dispatch: switch (instruction) { .load => { self.push(self.locals[self.read(u8)]); continue :dispatch self.read(Instruction); }, .load0 => { self.push(self.locals[0]); continue :dispatch self.read(Instruction); }, .load1 => { self.push(self.locals[1]); continue :dispatch self.read(Instruction); }, .load2 => { self.push(self.locals[2]); continue :dispatch self.read(Instruction); }, .load3 => { self.push(self.locals[3]); continue :dispatch self.read(Instruction); }, // ... } and this is in a situation where this level of performance optimization is actually valuable to spend time on. it's nice that Zig lets you achieve it while reusing the logic.
- loeg 1y agoJust having a comptime unreachable feature seems pretty cool. Common C++ compilers have the worst version of this with __builtin_unreachable() -- they don't do any verification the site is unreachable, and just let the optimizer go to town. (I use/recommend runtime assert/fatal/abort over that behavior most days of the week.)
- deleted 1y ago[deleted]