6 ms·
I like zig, but agree with many of the points here. A couple of thoughts below, > Zig reference documentation badly needs examples. Can't figure out how to us
by messe 5y ago
I like zig, but agree with many of the points here. A couple of thoughts below,
> Zig reference documentation badly needs examples. Can't figure out how to use std.fmt.parseInt.
While yes, Zig documentation badly needs examples, I'm not sure this particular criticism is justified. I would have thought that the usage of parseInt, was fairly obvious from the type-signature:
parseInt(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T
Or, translated to C++ (and assuming the use of exceptions to return the error):
template<typename T> T parseInt(const char buf[], uint8_t radix)
> Need to access myArray.items[idx] instead of myArray[idx]. I get it. But very unintuitive and requires knowledge of implementation details.
I'm 50:50 on this one. While it might be nice to hide the implementation here and have a .item(n: usize) member function, ArrayList explicitly manages a contiguous region of memory, so I don't see the problem with exposing it as a slice as part of the interface.
> Should I pass the allocator to every function? Doesn't seem great. Maybe I'm supposed to create a global? Globals are evil and feel bad.
As a rule of thumb, libraries should take allocators as arguments to their functions, while applications can either do that or create a global allocator. There is absolutely nothing wrong with using a global allocator in an application; after all it's what almost every other language does. Zig just makes that global explicit.
Globals aren't always evil.
> Zig's inability to infer type is annoying. If I create var count and return it and the function return type is usize then var count is obviously a usize.
It's not. It could be any unsigned type of smaller size than usize.
- flohofwoe 5y ago> It's not. It could be any unsigned type of smaller size than usize. I think it refers to Rust's ability to "indirectly" infer a type by it's later usage which can be somewhere entirely else in the function (for instance when it's used as return value), while Zig has a more direct type inference (but IMHO Rust's approach definitely isn't "obviously better", because sometimes one needs to read and understand the entire code of a function to get an idea what type a variable might be).
- messe 5y agoI understood that, but my comment could have been more clear. I more meant that it's not necessarily obvious that the type check should infer that it's a `usize` just because the function returns a `usize`. It could be any type that can be implicitly cast to a `usize` which in a 64-bit Zig system is any of `u[1-64]`.
- ayende 5y agoFor the record, I find Rust's ability to change the type of the variable _after_ it was declared to be absolutely bonkers for my ability to understand what is going on in the code. I understand what it is doing, but the fact that changing the return type of a method will change what previous code will compile to is something that I find extremely non obvious and surprising. Inferring what the type is from the current code, sure. That saves me typing, but from what happens elsewhere looks like time travel.
- ReleaseCandidat 5y agoInteresting, I never thought about it this way. I just always find it a PITA that Rust can't do global type inference and that I have to type most of the types for myself.
- tialaramex 5y agoThe trouble is, when you say it's OK to infer "from the current code" that includes your function call, which is the thing where you're surprised it happens. There's no coherent reason why it'd be OK for Rust to conclude that x is a u64 from let x = some_u64.add(6); // the u64 type implement Add returning a u64 and yet not OK for Rust to conclude that x is a u64 from let x = someFunction(); // someFunction was defined to return u64 You had to explicitly change the return type of the method to cause yourself a surprise, Rust won't chase this rabbit through a warren, every individual function has to declare the types of its parameters and returns, so if you'd changed the function body not its declared return type, it could not have caused any change in type inference in other functions.
- ayende 5y agoThat isn't the problem. This is the issue: let x = str.parse()?; someFunc(x); The function called here is dependent on what `someFunc` will accept. That means that I can change what values will be accepted here by changing a completely different piece of code.
- ReleaseCandidat 5y ago
- latch 5y ago> ArrayList explicitly manages a contiguous region of memory, so I don't see the problem with exposing it as a slice as part of the interface. It used to be that you'd get an allocator by taking a pointer to the allocator field (&gpa.allocator), in 0.9.0 that was changed to a function call (gpa.allocator()) and thus broke _a lot_ of things. Exposing all these fields seems like it'll cause challenges when trying balance backwards compatibility vs breaking changes.
- messe 5y agoIn most cases I would agree with you, but I think ArrayList is simple enough that backwards compatibility won't be a major concern. It's literally just a wrapper around slices that handles resizing for you, and there's no real reason for that to change. Exposing the slice underneath is a necessary part of its interface.
- Kwpolska 5y agoTo someone very familiar with the idea of slices and Zig, it might be obvious and sensible. But to me, unawareness of how slices work means I thought I could easily access garbage data if array capacity >= list length; and accessing the underlying data structure feels unclean and makes me think of hacky code. While the field of ArrayLists is unlikely to see any innovation, it certainly doesn’t make a good first impression to programmers not exposed to Zig before.
- messe 5y agoI think part of it comes down to mindset. If you think of ArrayList as an abstract container, then yes, you're totally correct that having the underlying data structure being exposed is a bit jarring and unexpected. But I don't think that's the right level of abstraction to come from. ArrayList is just a wrapper around a slice that handles allocation and deallocation for you: the underlying data structure is part of the contract of the type, allowing it to be almost a drop in replacement.
- hansvm 5y ago
- zppln 5y agoWhen I did some Zig a month or so ago, I had great help looking at the tests, e.g.[1]. That being said, the state of the documentation is also the reason why I gave up on it for now. But I'm sure it will improve over time. [1] https://github.com/ziglang/zig/blob/79628d48a4429818bddef2e86e2d7073d1955302/lib/std/fmt.zig#L1646 https://github.com/ziglang/zig/blob/79628d48a4429818bddef2e8...
- anonymoushn 5y agoI never thought about this until now, but I guess it's actually super weird that ArrayList doesn't expose a getter-by-index and an iterator and instead expects you to use .items.
- laserbeam 5y agoThe thing is... Every function in the std works with slices, not woth array lists. You pass slices to mem.tokenize(), you pass alices to mem.cmp(), you pass a slice to sort(). I think that's why ArrayList exposes it for you, so you can just use it as a regular slice everywhere. I, honestly, find that simple and liberating. I'm glad there's no other kind of accessor thing. Continuous memory is just fast, and if you have some first class support for working with it, it makes no sense to duplicate the api. But again, if you want a hashmap you don't get the slice, for the slice doesn't make sense there...
- forrestthewoods 5y ago> I would have thought that the usage of parseInt, was fairly obvious from the type-signature: Blog author here. In hindsight yes it's obvious. I think my problem was a lack of understanding of comptime types. They're a little different from C++ / Rust since they're passed as args. Looking back I think I found the Zig docs less "sticky" than other languages. The concepts are familiar but just new enough I don't understand and don't fully remember them. There's a point while working on AoC that I wasn't sure if Zig had first-class runtime types or not. It has comptime types and it also has a TypeInfo built-in with some degree of reflection? Is that runtime reflection maybe? I think partially? I honestly don't know. Either way I was confused. :)
- laserbeam 5y agoAs far as I know, typeinfo is used at compile time. In general, doing runtime type shenanigans involves implementing things like vtables, and some other weirdness. This breaks "no hidden control flow". If you do that at comprime instead, the thing that runs at runtime is just memory offsets. You can for example generate code that parses json into 30 types you know at compile time, or you can use things like hashmaps and arraylists and whatnot to do it at runtime. You get a lot of mileage by just doing the dynamic code away from runtime, and that's one of the benefits of comptime. And it's a slow thing to realize, also not necessarily something everyone cares about.
- anonymoushn 5y ago> It has comptime types and it also has a TypeInfo built-in with some degree of reflection? Is that runtime reflection maybe? I do wish this was explained more prominently! The way this seems to work is that Zig has a lazy runtime for Zig code at compile time that does some interning (such that identical types obtained separately are equal, identical strings obtained separately are equal, etc.), and some types such as type can only exist in this runtime, not in the actual uh "run time" runtime. TypeInfo is for iterating over struct, union, or enum definitions at compile time to generate code. You'd use it if you wanted to write a generic json parser and serializer or if you wanted to write a type that's like an arraylist but internally uses a struct-of-arrays layout. Both of these are in the standard library and make informative reading. TypeInfo itself is a union that can exist at runtime, but I don't think you can get into a situation where you have something at runtime and you don't know what type it is already. So actually using the TypeInfo at runtime may not be very useful. > Zig appears to not report compiler errors for functions that get optimized out. This is apparently caused by the laziness. The docs suggest using std.testing.refAllDecls(@This()) to have the compiler check unreachable code. I mean the actual usable docs at https://ziglang.org/documentation/master/#Nested-Container-Tests https://ziglang.org/documentation/master/#Nested-Container-T... rather than the automatically generated stdlib docs.