9 ms·
I also did AoC 2021 in Zig: https://github.com/avorobey/adventofcode-2021 https://github.com/avorobey/adventofcode-2021 One thing the OP didn't mention that I
by anatoly 5y ago
I also did AoC 2021 in Zig: https://github.com/avorobey/adventofcode-2021 https://github.com/avorobey/adventofcode-2021
One thing the OP didn't mention that I really liked was runtime checks on array/slice access and integer under/overflow. Because dealing with heap allocation is a bit of a hassle, I was incentivized to use static buffers a lot. I quickly figured out that I didn't have to worry about their sizes much, because if they're overrun by the unexpectedly large input or other behavior in my algorithms, I get a nice runtime error with the right line indicated, rather than corrupt memory or a crash. Same thing about choosing which integer type to use: it's not a problem if I made the wrong choice, I'll get a nice error message and fix easily. This made for a lot of peace of mind during coding. Obviously in a real production system I'd be more careful and use dynamic sizes appropriately, but for one-off programs like these it was excellent.
Overall, I really enjoyed using Zig while starting out at AoC problem 1 with zero knowledge of the language. To my mind, it's "C with as much convenience as could be wrung out of it w/o betraying the low-level core behavior". That is, no code execution hidden behind constructors or overloads, no garbage collection, straight imperative code, but with so much done right (type system, generics, errors, optionals, slices) that it feels much more pleasant and uncomparably safer than C.
(you can still get a segmentation fault, and I did a few times - by erroneously holding on to pointers inside a container while it resized. Still, uncomparably safer)
- geokon 5y ago"runtime checks on array/slice access and integer under/overflow" I'm probably missing something. I feel like you'd get this and a lot of the other benefits you list if you just compile C/C++ with Debug options - or run with Valgrind or something. Are you saying you get automatic checks that can't be disabled in Zig? (that doesn't sound like a good thing.. hence I feel I'm missing something :) )
- djur 5y agoSlices allow catching a lot of bounds errors that you can't reliably catch when using raw pointers.
- superjan 5y agoI would say that keeping the checks in runtime for release builds is the smart default. For most usages, removing the checks in release builds only adds security holes without measurable impact on performance.
- tialaramex 5y agoWhat "Debug options" are you imagining will provide runtime checks for overflow and underflow in C and C++ - languages where this behaviour is deliberately allowed as an optimisation? In C it's simply a fact that incrementing the unsigned 8-bit integer 255 gets you 0 even though this defies what your arithmetic teacher taught you about the number line it's just how C works, so a "Debug Option" that says no, now that's an error isn't so much a "Debug Option" as a different programming language.
- not2b 5y agoC unsigned integers are completely well behaved: they do arithmetic modulo 2^n, and I hope you had a teacher that exposed you to that. C has many problems but that isn't one of them: overflow of unsigned is designed and documented to wrap around.
- justinpombrio 5y ago> C unsigned integers are completely well behaved: they do arithmetic modulo 2^n Sadly, one rarely finds an excuse to work in the field Z_(2^32) or Z_(2^64), so while that behavior is well-defined, it's rarely correct for whatever your purpose is.
- naasking 5y agoArray indices should arguably be unsigned (and struct/type sizes), so I'd say it's a lot more common than you imply.
- AnIdiotOnTheNet 5y agoThe runtime safety checks are enabled in Debug and ReleaseSafe modes, but disabled in ReleaseFast and ReleaseSmall modes. They can be enabled (or disabled) on a per-scope basis using the `@setRuntimeSafety` builtin.
- typon 5y agoDefaults matter a lot. Just because something is possible doesnt mean it is likely to happen. Are most people going to enable asan, run their programs through valgrind extensively, or just do the easy thing and not do any of that? This is also why neovim is being actively developed and successful and vim is slowly decaying. The path of least resistance is the path most well travelled.
- snovv_crash 5y agoAny project with a decent test coverage and CI can easily set up an ASAN / Valgrind run for their tests. I know I've had this on the last few C++ codebases I've worked with.
- pcwalton 5y agoYou're correct: you do get virtually all of the safety benefits of Zig by using sanitizers in C++. (Not speaking to language features in general, obviously.) In fact, C++ with sanitizers gives you more safety, because ASan/TSan/MSan have a lot of features for detecting UB. Especially note HWASan, which is a version of ASan that is designed to run in production: https://source.android.com/devices/tech/debug/hwasan https://source.android.com/devices/tech/debug/hwasan
- pcwalton 5y ago> (you can still get a segmentation fault, and I did a few times - by erroneously holding on to pointers inside a container while it resized. Still, uncomparably safer) This is a severe problem, and I predict that this is going to cause real security issues that will hurt real people if Zig gets used in production before it gets production-ready memory safety. This exact pattern (pointers into a container that resized, invalidating those pointers) has caused zero-days exploited in the wild in browsers.
- skybrian 5y agoZig apparently has valgrind support. Maybe it’s not turned on by default?
- staticassertion 5y agoValgrind support is cool but it's not a solution to the problem.
- formerly_proven 5y agoI don't think Zig has any particular Valgrind support, it's just a binary after all. In order to properly utilize valgrind though you're going to have to change from the GPA or whatever allocator you're using to the libc one so that Valgrind can trace memory allocations correctly via preloading.
- skybrian 5y agoHere is some kind of valgrind API [1] and a here is a report from someone who tried using valgrind [2]. Yes, it doesn’t sound all that special. [1] https://github.com/ziglang/zig/blob/master/lib/std/valgrind.zig https://github.com/ziglang/zig/blob/master/lib/std/valgrind.... [2] https://dev.to/stein/some-notes-on-using-valgrind-with-zig-35c1 https://dev.to/stein/some-notes-on-using-valgrind-with-zig-3...
- AndyKelley 5y agoA better way to put it is "valgrind integration". It is enabled by default (when compiling for a target that has Valgrind support). Mainly it integrates with the `undefined` language feature which helps catch branching on undefined values. The nice thing you get beyond what C gives you, is that in Zig you can set things to undefined when you are done with them. Meanwhile in C, Valgrind is only aware of undefined for uninitialized variables. But as others have pointed out, although Valgrind is a nice debugging tool, you would not run your application in it under normal circumstances. It's also not available on some important targets, such as macOS and Windows.
- rslabbert 5y agoFor what it's worth, I find a lot of Zig code benefits from switching to u32/u64 indexes into an array instead of using pointers. This is only really doable if your container doesn't delete entries (you can tombstone them), but the immediate benefit is you don't have pointers which eliminates the use after free errors you mentioned. The other benefit is that you can start to use your ID across multiple containers to represent an entity that has data stored in multiple places. See [1] for a semi-popular blog post on this and [2] for a talk by Andrew Kelley (Zig creator) on how he's rebuilding the Zig compiler and it uses this technique. [1] https://floooh.github.io/2018/06/17/handles-vs-pointers.html https://floooh.github.io/2018/06/17/handles-vs-pointers.html [2] https://media.handmade-seattle.com/practical-data-oriented-design/ https://media.handmade-seattle.com/practical-data-oriented-d...