4 ms·
I'd be interested in seeing a code comparison of equivalent Rust and Zig code with regards to tagged unions, to demonstrate your claim. I could help you with th
by AndyKelley 8y ago
I'd be interested in seeing a code comparison of equivalent Rust and Zig code with regards to tagged unions, to demonstrate your claim. I could help you with the Zig code if you provide the Rust code.
Also to answer your questions:
> As far as I can tell there's no type inference?
You can see an example of type inference here: https://ziglang.org/documentation/master/#Hello-World https://ziglang.org/documentation/master/#Hello-World
> does Zig allow messing with types at runtime?
Yes. https://ziglang.org/documentation/master/#Generic-Data-Structures https://ziglang.org/documentation/master/#Generic-Data-Struc...
Edit: my mistake, I misread or didn't see the "at runtime" part. It only allows messing with types at compile-time.
- simias 8y ago>I'd be interested in seeing a code comparison of equivalent Rust and Zig code with regards to tagged unions, to demonstrate your claim. I could help you with the Zig code if you provide the Rust code. Well in Rust you don't have to define the enum and the union separately, actually in Rust unions and enums are effectively the same thing (outside of C FFI that is). So you in Rust you have something like: enum ComplexType { Ok(u8), NotOk, } Then to unpack it you always have to destructure it, you can't just do my_complex_type.Ok() or something like that. You can create accessor methods if that makes sense for your enum though, for instance for this ComplexType you could implement an "unwrap" function that returns the u8 in case of success and panics in case of an error. In general you tend to avoid those unless they really make sense, as a rule of thumb it's better to "match" the type (which seems similar to Zig's switch statement with enums). Beyond that I dug a bit deeper in the docs and I don't quite understand how generics are supposed to work exactly, at first they look like what I'm used to in other languages but then I get to the generic printf example and I see this code: pub fn printValue(self: *OutStream, value: var) !void { const T = @typeOf(value); if (@isInteger(T)) { return self.printInt(T, value); } else if (@isFloat(T)) { return self.printFloat(T, value); } else { @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); } } Is it just for the sake of the example or does it just exhibit a limitation of the generic system? What would happen if I wanted to be able to print a custom type for instance? Also, if generics exist, why are things like "@clz" or "@intToFloat" compiler builtins, surely they could be implemented as part of the stdlib using a generic method? Although I see that "@memset" and "@memcpy" are also builtin and I can't quite figure out why so maybe I'm missing something here.
- AndyKelley 8y agoIn Zig: const ComplexType = union(enum) { Ok: u8, NotOk, }; It's recommended to use destructuring syntax to unpack it, but you have the option to use fields directly which invokes runtime safety checks. This matches the syntax for normal unions, which do not have tags, but still have runtime safety checks. Rust does not have this feature - unions must always have tags - where as in Zig the state that determines which tag is active in a union could be somewhere else. > What would happen if I wanted to be able to print a custom type for instance? const std = @import("std"); pub fn main() void { const ComplexType = union(enum) { Ok: u8, NotOk, }; var ct = ComplexType { .Ok = 123 }; std.debug.warn("{}\n", ct); ct = ComplexType.NotOk; std.debug.warn("{}\n", ct); } Outputs: ComplexType{ .Ok = 123 } ComplexType{ .NotOk = void } This is all implemented in userland in the standard library. Last time I checked, in Rust the equivalent code is implemented with a macro hard coded in the compiler. > why are things like "@clz" or "@intToFloat" compiler builtins, surely they could be implemented as part of the stdlib using a generic method? They could be implemented in userland, but then they would possibly not lower to the LLVM primitives that they represent. In the case of the casting functions, it's more to do with semantics of the language, making it hard to accidentally shoot yourself in the foot. Shooting yourself in the foot on purpose, however, is always allowed.
- simias 8y ago>This is all implemented in userland in the standard library. Last time I checked, in Rust the equivalent code is implemented with a macro hard coded in the compiler. You're right and it's still the case as far as I know but I was thinking more about having a customized output, in Rust you can implement the Display interface and completely customize the way the object is serialized for output. Your sibling comment by AnIdiotOnTheNet shows how to do just that but is it done purely in stdlib or is it actually implementable in the language itself? If it is actually doable in pure Zig I'd recommend showcasing that as part of the "generics" docs because as it is it's a bit odd to have example code that ends up exhaustively matching possible types, it kinds of defeat the purpose of generics and is the cause of my confusion. Thank you for your answers by the way, they're appreciated. I'm very happy to see all this experimentation around low level "light runtime" languages lately, it's definitely sorely needed. I hope that 20 years from now C won't still be the alpha and omega of low level code.