4 ms·
Lack of destructors/RAII is the deal breaker for me. I know that this causes a function call to be made when a variable goes out of scope, which is "hidden con
by Dwedit 3y ago
Lack of destructors/RAII is the deal breaker for me. I know that this causes a function call to be made when a variable goes out of scope, which is "hidden control flow" and therefore banned by the language design.
- pixelpoet 3y agoSame with lack of vector operators for me. I've asked a lot :(
- anonymoushn 3y agoWe have them.... for SIMD vectors.
- pixelpoet 3y agoThis forces us to use homogeneous 4D vectors for 3D ops ({vec3, 0} for directions and {vec3, 1} for points), which basically wastes registers when we're doing the right thing with the right vec3 types on a e.g. 32-wide vector machine. If Zig really wanted to do the best job here, it should recognise that we're semantically not using this 4th padding dimension if we're not doing 4D projective stuff. If it really is going to force you up to 4D space, then it should be doing compile time checks e.g. sorry, you tried to add point plus a point which makes no sense, unlike vectors, or vectors and points. This might become more important if Zig to GPU code translation becomes a thing...
- anonymoushn 3y agoRight, clearly SIMD vector primitive ops are not what you want for a 3d math lib.
- zozbot234 3y agoIf you can avoid all hidden control flow, that's actually a great choice. Rust couldn't do this originally because it needed panics, which must auto-destruct objects on the stack.
- bluGill 3y agoNo it is not - because it means you don't get RAII and so you have to clutter your code with calls to destruct things. In real code there are often several paths that things could be destroyed in and so there is a lot more manual code. Code that we know from experience people often forget to write. (memory leaks are a thing).. Okay, you can solve the forget problem with static analysis. But since in most cases I don't care about destruction so long as it happens, when reading the code I prefer this hidden. Abstraction means hiding details and done right is a great thing. Yes abstraction often is abused to hide the wrong thing, but that isn't the fault of abstraction as a concept.
- norir 3y agoI prefer explicit blocks for automatic object destruction to RAII. For example, withThing { thing => doStuffWith(thing) } This makes it clear to me that the object is only valid within this block. There is no copy constructor madness where I might completely lose track of where the object might be alive and in scope.
- vacuity 3y agoNote that C++'s design of RAII and Rust's design have important differences.
- jjnoakes 3y agoThe problem with blocks is that you can forget them.
- fl0ki 3y agoRust does keep track of where the object is alive and in scope, even if that happens to be decided dynamically, e.g. an object may only exist in a condition branch or as the result of a loop running at least once. Objects also won't be silently copied unless they are types that explicitly marked as trivial to copy (i.e. bytewise). It then also guarantees cleanup if the object actually does exist. Rust already does what many people want for deterministic, safe cleanup, which like C++ includes RAII, but unlike C++ is not limited to RAII.
- SomeCallMeTim 3y agoHonestly, most everything listed on the page as an advantage of Zig, is a disadvantage from my point of view. I'm sure Zig has its use cases. For what I write, I not only don't care if there's a hidden function call or error handling, I see those as 100% necessary for a modern language. Needing to handle errors inline is a huge mess for anything nontrivial. It distracts from the logic that's important at that point in the code. Being able to override an accessor to do something instead of being a raw access is incredibly useful; a tiny change and rebuild is all that's required to track information that you would otherwise need to rewrite an entire app to support. If you're writing extremely low level code and libraries, especially embedded, then fine, minimizing hidden behavior is important. Being able to operate without a standard library is also important in that case. Outside of that niche, though, there are few places I'd call those "features" of Zig an advantage.
- throwawaymaths 3y ago> Needing to handle errors inline is a huge mess for anything nontrivial Maybe try zig first before you make such a blanket statement?
- anonymoushn 3y agoZig's error handling is unobtrusive. You can just write `try` if you want to propagate errors instead of handling them in some specific way.
- EasyMark 3y agoYeah zig just doesn't offer enough advantages over "I'll just use c++ as a better c with raii, containers, safe string class, template functions, and very simple classes/powerful structs (no inheritance), and a threading standard library"
- lenkite 3y agoLack of proper interfaces/traits is also a deal-breaker. One appears to need to do un-readable hacks to get a basic interface in Zig.
- zozbot234 3y agoInterfaces/traits is syntactic sugar for a function dictionary/vtable. It could probably be done via Zig's compile time subset.
- lenkite 3y agoStandardized language-defined syntactic sugar is deeply important - esp for something as important as interfaces/traits - which are the foundation blocks for setting component contracts in libraries and modules.
- colonwqbang 3y agoNot if the standardised way is worse than the naive way, like in C++. Then I will rather take manual virtual calls, like in C: https://elixir.bootlin.com/linux/latest/source/include/linux/fs.h#L1916 https://elixir.bootlin.com/linux/latest/source/include/linux... It isn't that much more work and it's not something you end up doing terribly often. I think destructors are a much more important to help day to day development and avoid bugs.
- steveklabnik 3y agoIn Rust, traits can be used via a vtable, but also (and the vast majority of the time) as regular old statically dispatched functions. That doesn't mean Zig couldn't do something similar if they want to, just pointing out that it's not always the case that it means dynamic dispatch.
- zozbot234 3y agoI didn't mention run-time dynamic dispatch, only a function dictionary (which C++ calls a vtable). Using traits for bounded subtype polymorphism is much like having a dictionary of functions that can be resolved completely at compile time, making it possible to use monomorphization and static dispatch.