18 ms·
Problems of C, and how Zig addresses them
- jjgreen 3y agoEarlier discussion https://news.ycombinator.com/item?id=36497574 https://news.ycombinator.com/item?id=36497574
- nunuvit 3y agoIs a single comment really a "discussion"?
- sharikous 3y agoI think it should be const x: i16 = -1 * 32768; // valid In the text it's i32
- kevin_thibedeau 3y agoThat explanation is bogus anyway. It's a unary operator applied to a positive integer literal. No multiplication involved. In this case the range issue can be addressed in C by using the "L" suffix to upgrade the literal to 32-bit and let the compiler truncate it back to 16-bit.
- ARandomerDude 3y agoHug of death. https://web.archive.org/web/20230703133352/https://avestura.dev/blog/problems-of-c-and-how-zig-addresses-them https://web.archive.org/web/20230703133352/https://avestura....
- revskill 3y agoOK, so Zig to C is like Typescript to Javascript.
- sgeisenh 3y agoI don't think I fully understand the analogy. Typescript has almost identical semantics to Javascript but adds typing syntax to improve developer experience and make it easier to manage a large-scale JS codebase. Zig is a fundamentally different language than C that has a lot of new features. Two great examples are comptime and allocators which have complements in C, but are really very different from what C provides. If you're suggesting that zig is aiming to provide a C alternative with better ergonomics, then I agree; but zig and C have a lot more differences than Typescript and Javascript.
- throwawaymaths 3y agoTypescript also compiles to C, zig explicitly seeks to replace C
- esrauch 3y agoZig has more things for C interop than you're suggesting though, namely the extern keyword, calling convention, etc. Like ts/js, zig/c seems first class designed be used together in one codebase, compared to eg Rust where is obviously possible but more deliberately discouraged (since it breaks the safety guarantees, which Zig never claimed to begin with)
- flohofwoe 3y agoTS is just modern JS with type annotations. Zig OTH is "just C" but with a modern syntax, much more correctness, comptime, reflection, generics, a rich standard library, an integrated build system, cross-compiling that 'just works'. (I'm sure I forgot a couple of things)
- pjmlp 3y agoNaturally it doesn't show how it tackles use-after-free cases.
- gorjusborg 3y agoRather than offer drive-by criticism, perhaps you could illustrate 'the problem' you are suggesting is being hidden?
- tmtvl 3y agoThe problem I believe is being discussed is that dereferencing a pointer after the memory it references is freed does not throw a warning at compile time. This means the programmer has to manually keep track of pointers, making sure not to free the relevant memory until there are no more references to it lingering about. While there are programming techniques which to lesser or greater extent prevent problems from occurring, e.g. by allocating all memory needed at the begin of the program and freeing all of it at the end, doing no (de-)allocation while the program is running; such solutions still require some discipline on part of the programmer and for some it is a tall ask.
- k__ 3y agoSo, Zig set out to solve all but the biggest issue with C?
- tmtvl 3y agoThat seems to be what OP is implying. I haven't used Zig, so I can't say. I suppose people who want to avoid memory management related bugs who can't acccept a GC language can use Carp.
- flohofwoe 3y agoThe biggest issue with C is arguably buffer overflows, not use-after-free (Zig offers spatial but not temporal runtime memory safety). If you want both, but don't want a Rust-style borrow checker (with all the restrictions this entails), use 'tagged-index-handles', those work just fine across all languages (and even make sense in Rust): https://floooh.github.io/2018/06/17/handles-vs-pointers.html https://floooh.github.io/2018/06/17/handles-vs-pointers.html
- bodge5000 3y agoI'm no expert on either languages, but I tried Zig for the first time properly the other day. I really liked it up until I hit hashmaps. One thing that I think goes under-appreciated about C (and other languages with a similar paradigm) is thats its pretty upfront and clear about what it can and can't do out of the box. If you want hashmaps in C, you need to create your own implementation, otherwise think of a way round them. In Zig meanwhile, they exist in the standard library, but at least for me, were more work than they were worth to work with. The result was the same, when making my test project in both C and Zig I ended up not using hashmaps, the difference is that in C I came to this decision in 2 minutes rather than 2 hours. I imagine if I had more experience in Zig, none of that would be a problem, and its not aiming to be a beginner-friendly language anyway, but that was just my experience of it so far
- hazebooth 3y agoWould you mind elaborating on the complications you ran into? I ask because I think you used std.HashMap instead of std.AutoHashMap, the latter of which automatically chooses a hash function based on the types provided.
- bodge5000 3y agoIt was a little over a week ago now so my memory is a bit hazy, but I'll try to the best of my knowledge. Without getting into the weeds of why (happy to do so, just want to keep this readable), basically I needed to define and populate a hashmap in a new script and then import it into my main script, which to my mind left me with two options: * Define and initialise it at the same time (my preferred method) as a constant. I don't have much to say on this as iirc, I had no luck with it at all, never even got close. * Define it in a (public) function, add each field in with "put" and then return the hashmap. I tried with std.AutoHashMap and various other things, but to what I could work out there was no type of hashmap, so it wouldn't accept my return type.
- ForkMeOnTinder 3y agoHere's how you return a hashmap from a function: fn buildMap(allocator: std.mem.Allocator) !std.AutoHashMap(u64, u64) { var result = std.AutoHashMap(u64, u64).init(allocator); errdefer result.deinit(); try result.put(10, 100); try result.put(20, 200); return result; } Your #1 option should be possible once Zig has comptime allocators -- it's on the roadmap, but not possible yet iirc.
- Lyngbakr 3y agoThat was a nice, clear explanation that even I could understand. One thing I wasn't sure about, though. var arr = [_]u32{ 1, 2, 3, 4, 5, 6 }; // 1, 2, 3, 4, 5, 6 const slice1 = arr[1..5]; // 2, 3, 4, 5 const slice2 = slice1[1..3]; If I were to use indices out of range, presumably that would that give me a compile time error? Could I use variables as the indices and, if so, could I get a seg fault at runtime or are there some sort of guards on there?
- ForkMeOnTinder 3y ago> If I were to use indices out of range, presumably that would that give me a compile time error? Yes, if all operands are comptime-known (or comptime-length-known), bounds checks happen eagerly at compile time. > Could I use variables as the indices and, if so, could I get a seg fault at runtime or are there some sort of guards on there? Yes you can use variables. If they're out of range, behavior depends on the build mode: - In Debug and ReleaseSafe, you get a guaranteed panic (which aborts the app with a stack trace) - In ReleaseSmall and ReleaseFast, you get undefined behavior On their discord I suggested a version of slicing that bounds checks in all modes (something like `slice?[1..5]`, syntax doesn't matter) and returns an optional slice, which is null if the bounds are out of range. But they didn't seem too keen on it. So I've been using a little wrapper function instead.
- throwawaymaths 3y agoWhy not just compile your whole program safe and then demarcate which functions you want to not be safety checked?
- estebank 3y agoCareful, you're dangerously close to suggesting an effects system. If the function explicitly marked as doing unsafe math operations calls another that doesn't specify a preference, should its math operation be safe or unsafe? If the former, this makes it likely a better default, but limiting in its usefulness. If the later, then your compiler now has to keep track of this information for the entire call graph.
- travisgriggs 3y agoTangentially... I'm grateful that these new batches of systems languages have chosen to use the u8, i32, etc, style integer types. Rather than the uint8_t and int32_t style. First thing I do in any of my low level embedded C projects is put in the various uNN/iNN types.
- pavlov 3y agoThe C standard effectively can’t add new types without the “_t” suffix, so they’re stuck with limitations that a brand new language won’t have.
- pitaj 3y agoCan't they add a new header file?
- flohofwoe 3y agoWhat if a library API needs to include this new header because it wants to use the new typedefs in its API declarations but your own code (which needs to include the library header) also has its own 'u8' typedefs? (PS: it's probably ok because C compilers seem to accept redundant typedefs)
- sieabahlpark 3y agoIt sounds like you got some refactoring to use since you didn't namespace your own core types?
- flohofwoe 3y agoC doesn't have namespaces. The only sane way to deal with name collisions is either to use C stdlib types in library APIs (e.g. uint8_t), or use a library specific prefix (e.g. mylib_u8) - which is of course even more awkward than just using the standard uint8_t.
- gwenzek 3y agoI wrote a similar article but focused on how Zig design enable better optimizations than C. https://zig.news/gwenzek/zig-great-design-for-great-optimizations-638 https://zig.news/gwenzek/zig-great-design-for-great-optimiza...
- SmoothBrain12 3y agoMeh. In your article, you could always mark the function f() as pure or const and get the hoist
- square_usual 3y agoYou can repeat "C is a minimal abstraction over assembly" as many times as you want, it doesn't make it true.
- pavlov 3y agoI’m just old enough to remember when C and Pascal were called high-level languages. “C is actually assembly” was quite a plot twist from there.
- vanderZwan 3y agoYou can thank Rob Pike for that, about a decade ago he wrote a Google Plus post arguing that one of the things C brought to the table was that it essentially was portable assembly compared to most other high level language projects at the time. I guess the tiny sliver of nuance that the "essentially" added was quickly lost.
- quirino 3y agoCould you specify the main ways it isn't? I have limited experience in both but I haven't seen much that suggests otherwise
- chc 3y agoMany features of C do not directly correspond with most modern assembly languages. You cannot predict the exact assembly it will generate without knowing a lot of details about your compiler and platform, and even then it's often iffy. It seems like a bit of a leap to call something "a minimal abstraction" if you can't even correctly describe how an operation in the abstraction corresponds to operations in the lower level.
- flohofwoe 3y agoThat's mainly the result of optimizer passes, C itself doesn't have much to do with it. Assembly languages actually haven't changed all that much since the 70's, but compilers have improved a lot. The output of early C compilers did indeed match the source code quite closely (and not just on the PDP-11), but even today that's true if you disable optimizations (and even with optimizations is usually pretty straightforward to map the C source to the assembly listing - if you're somewhat aware what optimizer passes in modern compilers are doing). Of course CPU ISAs are already human-friendly abstractions over what's actually happening down in the hardware.
- augustk 3y agoDoes Zig have a formal grammar (like C)?
- fallat 3y agoThere's a grammar that tries its best to be maintained alongside the language but it's nothing solidified yet.
- quelsolaar 3y agoTechnically there is no "compile time" in C. Any part of the program can be computed at compile time or at runtime. Its entirely up the the implementation. The AS-IF rule in the C standard lets implementations do whatever they want as long as the program outputs the same things. The pre-processor, const expressions, constexpr (a for this reason compliably broken feature in my opinion), even text parsing and tokenization can happen at run time. Not having the standard define what is compile time and not is a feature not a bug, and one of the reasons that C continues to enjoy healthy compiler development. (I'm not taking a stand for or against Zig, other then that i support the development of new languages)
- steveklabnik 3y agoC23 includes constexpr for some constructs, by the way.
- quelsolaar 3y agoI know, and its very broken.
- steveklabnik 3y agoIn what way? I haven't had the opportunity to try it out myself yet.
- quelsolaar 3y agoIt is a feature that sounds like it promises to make something compile time but it doesn't. At the same time it has a bunch of limitations, that in no way reflects what modern compilers can do. Plenty of times you can write an expression and if you say its a constexpr, the compiler is forced to tell you that its not a valid constexper, but if you just remove the constexper qualifier, the compiler the compiler can solve the expression at compile time just fine. So using constexpr gives you: -No guarantee that its computed at compile time. -Limits what the compiler lets you do when using the constexper keyword. -Adds no performance benefits or guarantees. -Makes your code not portable to most C compilers. On top of this it adds implementation burden for compiler, and complicates the specification significantly.
- sfpotter 3y agoC is standardized and is one of the most (the most?) widely used programming language on the planet. If you write code targeting a C standard and don't include many unstable dependencies, it has a good chance of running correctly for a very long time. If you move from C to Zig, you lose that stability. Are Zig's convenience features really enough to compensate for this loss?
- jorangreef 3y agoOne of the main reasons, in fact, that we picked Zig for TigerBeetle (over C, which was the alternative, given we needed to handle memory allocation failure), was because of Zig's excellent interoperability with the C ABI. For example, we write the reference TigerBeetle client implementation completely in Zig, then wrap this with the C ABI, and then bind to this C ABI from all target languages, to increase our velocity in how quickly we can ship language clients. More details, in general, around our clients here: https://tigerbeetle.com/blog/2023-02-21-writing-high-performance-clients-for-tigerbeetle/ https://tigerbeetle.com/blog/2023-02-21-writing-high-perform...
- sfpotter 3y agoSounds cool but doesn’t address my question. Do you think the relative instability of Zig will hurt you? Or do the benefits outweigh the costs? Lots of languages make it easy to talk C ABI (including… C itself). So why Zig? I guess the people downvoting my assume I’m hostile to Zig. I’m not. I’m saying that the stability of C is a selling point and I’m curious what features of Zig are so valuable that people are giving that up.
- menaerus 3y ago> given we needed to handle memory allocation failure Isn't it that the OOM killer is likely to be a much bigger problem? With the overcommit enabled and OOM enabled I don't think I can envision the case where you would run into a failed allocation - allocations on Unices practically never fail. It is the OOM-killer that will likely kill your process once it detects that the pressure on your ram+swap is high so you won't even have a chance to run into the OOM. OTOH if you disable the OOM-killer there's another and much bigger problem to solve - a kernel panic. I guess that's not the condition you want your system to run into. So, I think that the only combination where you can deterministically detect and run into allocation failures is when both OOM-killer and overcommit are disabled. That's what I think Windows is doing by default.
- matrix_overload 3y agoIt is nice and innovative, no doubt. But reinventing the syntax from scratch instead of introducing just the minimal amount of changes to the original C is a barrier that will deter 90% of possible adopters. The reason C# took off is that it's as close to C/C++ as possible. If there is a difference, it's due to a fundamental semantic change. E.g. it moved from painstakingly reinterpreting hundreds of small header files for each compiled source file to loading efficiently serialized hierarchies of class definitions. Hence #include got replaced with using. It changed the semantics of pointers vs. references, hence 'ref' instead of '*', and so on. But there is no "fn square(x: u32)" instead of "uint32_t square(uint32_t x)" just for the sake of it. And that's the main reason why many people will never consider even looking into the advantages Zig offers. Keeping another syntax in your head is just not worth it.
- lerno 3y agoWhat do you think about C3 then? https://c3-lang.org https://c3-lang.org
- Joker_vD 3y agoCounterpoint: C itself invented lot of syntax compared to the contemporary ALGOLs and PL/I. Yet, it became immensely popular.
- matrix_overload 3y agoI would dare say, different combination of early adopters/pragmatists in your target audience. C/C++ these days is mostly legacy stuff, so if you want to cater to that audience, you deliver small incremental improvements. Many people that could be bothered to learn Zig syntax, jumped ship to Python/Java/whatever already.
- Night_Thastus 3y agoZig and Python/Java are completely different beasts. One is a low-level systems language on the order of Rust or C. The other two are much higher level, easier to work with languages more attuned to desktop, mobile applications and enterprise work. I don't think anyone is seriously "jumping ship" from Zig to those.
- astrobe_ 3y agoAbout macros and comptime: why is the compiler unable to detect that a function is a pure function, and if it is called with constants, the result can be computed ahead of time? At least in the simple cases such as the square example, which are I believe quite common, that would work. Maybe still add an annotation to make sure the compiler will do what the user expects, because humans are terrible compilers after all. Because the "footgun" here is really that when using macros we are switching languages and strategies - in one case it's eager evaluation, and in the other is sort of like lazy evaluation.
- throwawaymaths 3y agoDo you mean in zig? It's possible to do arbitrary, bounded computation (compiler quits if you expend "too many tokens") at comptime in zig, unless there is strange statefulness. This is sensible since you want changes in, e.g. your code tree to taint compiled resources. If your code can go read from the filesystem in an untracked way, you break the incremental compilation model.
- astrobe_ 3y agoThis was sort of a general question, although Zig confused me a bit by placing the annotation at the call site rather than on the function declaration. I guess this choice was made to keep compilation times under control.
- throwawaymaths 3y agoA function should not have to be labelled as comptime to be run at comptime. There are cases when you might want to run the same function at comptime as at runtime (for example, if you want to do a thing with algorithm X but cache the result for a series of trivial cases as a lookup table). Putting it at the callsite is more explicit actually and for example generating precompiled arrays clearly ties comptimeness to the actual artifact created instead of making it inferred from the signature of the function (which may be very far away in code)
- flohofwoe 3y ago
- LexiMax 3y agoOne thing that was unclear to me based on my prior noodling with Zig was what the plans were for supporting interface or trait-based polymorphism. I've been falling in love with C++'s std::experimental::is_detected and am looking forward to being allowed to use C++20 in my environment since it will bring Concepts, but in my cursory examination it seems Zig seems to prefer vtable abstractions like Allocator.VTable.
- v4vvdq 3y agoZig does have some kind of interfaces, check [1]. I am interested to learn, how Traits in Rust and Interfaces in Go behave differently from this concept. [1] https://github.com/ratfactor/ziglings/blob/main/exercises/092_interfaces.zig https://github.com/ratfactor/ziglings/blob/main/exercises/09...
- LexiMax 3y agoThis appears to be done by union-ing the types in question. The thing about Go interfaces (and C++20 Concepts) is that you can name a type that contains certain methods or behaves a certain way, but you don't actually have to inherit from the interface or concept explicitly - anything shaped correctly that conforms to the interface or concept will work. And at least with Go, if you try and pass something in that doesn't conform to the interface, it is very particular about telling you what you're missing. One downside of C++ templates is that if you have a problem with what you're passing in, you might get a horrendous error message somewhere deep in the implementation - or worse, your code might compile just fine, but have unexpected behavior - instead of a nice "Hey, you need to add a method named `foo` to this type."
- pixelpoet 3y agoI'm super sold on Zig except that it doesn't make graphics/vector coding with operator overloading possible :( I've heard what Andrew Kelley has to say about it ("that ONE little feature from C++...") but it's just a very sad situation for what otherwise looks like a lovely basis for graphics coding. Actually, I don't even want operator overloading in general (leading to stuff like the C++ stream API), it's JUST for vector stuff (ideally with swizzles); same for all the shading languages. Perhaps it's doable to make a little domain-specific language (DSL) in Zig for this purpose? With vector extensions (and as floh [hi!] points out, also matrices please) I'm 100% ready to seriously consider transitioning from C++, given the smooth C integration.
- flohofwoe 3y ago+1 (don't want general operator overloading, but a more powerful @Vector builtin type, basically Clang's ext_vector_type: https://clang.llvm.org/docs/LanguageExtensions.html#vectors-and-extended-vectors https://clang.llvm.org/docs/LanguageExtensions.html#vectors-...), plus maybe a similar @Matrix builtin up to 4x4.
- xgkickt 3y agoAre Clang’s builtin types interoperable with __m128 intrinsics?
- Gibbon1 3y agoMaybe I'm small brained but built in vector types align with heavily platform optimized libraries. Meaning you don't want this stuff implemented in the compilers front end. You want it handled in the compilers back end.
- pixelpoet 3y agoPerformance is equivalent in both representations, but the important thing is being able to write vec4 c = a * 4 + b; instead of vec4 c = vec4_add(vec4_mul(a, 4), b); i.e. infix vs postfix order, with simple * and +/- operators etc. I would very much like to appeal to the Zig authors to see the beauty in the former expression compared to the latter, for a huge class of real-world mathematical applications.
- Dwedit 3y agoOne thing Zig is missing is Exception Handling. Now before you complain about exception handling grossly bloating the size of a program, know that there are ways to trigger exceptions that do not involve the "throw" keyword. You get processor exceptions when you access a bad pointer, divide by 0, etc. If you don't want the program to instantly terminate, you need to be able to handle those exceptions. So far, it seems that the only way to handle exceptions is to use another programming language. Zig allows you to mix in C or C++ code.
- masfuerte 3y agoI know basically nothing about Zig, but you don't need language-level exceptions to handle platform exceptions. You can write the handler code in C. Why can't you write it in Zig? The usual problem is that the language runtime gets in the way, but I thought Zig had a very minimal runtime like C.
- Dwedit 3y ago__try and __except are win32-specific language extensions for C that do not exist in Zig. They become code which adds a linked-list item (exception handler entry) somewhere into the TEB. Win32 requires that you call all the Stack Unwind entries within your exception handler. When your exception handler is done, you longjmp out, or return to the offending code. Using the language extensions for exception handling is far more friendly than doing it manually with low-level code.
- anon-3988 3y agoI am curious why anyone still thinks that exception is superior/better/desirable than a simple typed functions (like Result<T,E> or Option<T>). > You get processor exceptions when you access a bad pointer, divide by 0, etc. If you don't want the program to instantly terminate, you need to be able to handle those exceptions. The processor does a lot of other things too, all of these functions can simply be wrapped with the appropriate Result<T,E> and Option<T>. Hell, I'd argue that for a system programming language all mathematical operations should return Result<T, ArithmeticError>. How is a try/catch better than a pattern match on the exact errors that you care about? inb4 checked exception, which is just an inferior version of algebraic data types.
- andrewedstrom 3y agoI remain confused about how Zig makes it to the front page of Hacker News so often. As far as I know, no one is using it in production after 7 years of development. Are people just really excited about this project? Are the people behind it just really good at marketing? Honest question: why do we all keep talking about Zig?
- mtlynch 3y agoTigerBeetle is using Zig in production.[0] I imagine some of Zig's other corporate sponsors are using it as well.[1] [0] https://tigerbeetle.com/ https://tigerbeetle.com/ [1] https://ziglang.org/zsf/ https://ziglang.org/zsf/
- pixelpoet 3y agoAndrew Kelley's intro[0] is probably the best talk I've ever seen, and his continuing leadership seems really great to me and worth supporting. Besides my reservations for its use in graphics programming (see my other comment) it looks very attractive in many practical ways (build system!! fuck CMake!) I could go from how I currently write my hobby code, sometimes a prototype for future commercial code. [0] https://www.youtube.com/watch?v=Gv2I7qTux7g https://www.youtube.com/watch?v=Gv2I7qTux7g
- PartiallyTyped 3y agoThis is easily one of the best programming talks I have ever seen, I loved the "no compiler magic" compile-time-known string formatting, and generics literally becoming a thing without magic either.
- Cyberdog 3y agoI can't answer you why Zig keeps showing up on HN (except for the surface-level answer that people keep submitting and upvoting it) but in terms of not being used in production, the Bun project, a Node/Deno alternative, is seeing a good deal of momentum by the people who like JavaScript a bit too much. It's probably the most widely used Zig project so far, including in production. https://bun.sh https://bun.sh
- igor_akhmetov 3y agoI really want to like Zig. Honest question: why is Zig so resistant to some kind of RAII? RAII is the biggest improvement that C++ brings over C. Leaving it out of a system programming language 50 years later is just strange? And no, manual defer is not the solution.
- throwaway17_17 3y agoThe most frequently given reason is that a core principle for Zig is ‘No implicit control flow’ or just the more general ‘Explicit is always better’. RAII, both for constructors and destructors are inherently implicit control flow constructs, also they usually involve access to allocation primitives, which Zig also rejects when implicit. You can disagree with the language principles if that is one’s position. However, the ‘resistance’ to RAII is a clear effect of those principles.
- gpderetta 3y agoI assume that zig requires the programmer to also manually allocate and free stack frames? And it eschews functions as they can hide allocations and control flows?
- schemescape 3y agoI would assume "allocation primitives" is referring to the heap.
- mk12 3y agoI felt that way at first too but I’m less concerned about it the more I use Zig. Defer and errdefer are already a huge improvement over C. I won’t deny there’s more risk of forgetting to clean up than in Rust or C++, but Zig’s approach forces me to think more clearly about what’s going on. For example, instead of doing a slow tree traversal to free little bits of memory all over the place (which RAII would do implicitly), in Zig I’d probably use an arena and free it all at once. A similar principle applies to other non-memory resources too (though maybe not mutexes for example). I also think it would be difficult to achieve the flexibility of Zig’s unmanaged containers (where deinit takes a parameter) in a RAII language.
- tayistay 3y agoFrom the article: const result = comptime square("hello"); // compile time error: type mismatch ok, cool. But if the error occurs deep in some hierarchy of comptime calls, do you get the same kind of long errors that you do with C++ templates? Does zig have a way of achieving better ergonomics? One nice thing about generics in Rust and Swift is they are constrained by traits/interfaces so you get a concise error at the call site.
- lerno 3y agoThis isn't about Zig, but I found that I prefer something similar to concepts over traits/interfaces, as the former are somewhat limited as to what they express. /** * @checked x & 1 "The input parameter must allow bit operations" */ macro bool is_power_of_2(x) { return x != 0 && (x & (x - 1)) == 0; } In the example above (disregard the fact that it's placed in the doc comments) we'd get an error if `x & 1` is invalid (doesn't pass semantic checking). This is the simplest possible example. Basically if you pass in a float or a struct you would then get "The input parameter must allow bit operations" rather than a dump of errors from the macro body. We can imagine other constraints, such as checking the value (if constant) to conform with valid ranges and so on. This is more of a "contract" style constraint that can be placed directly at the call location, rather than pushing down the check further down into macro body, for example something like this: macro bool is_power_of_2(x) { $assert($checks(x & 1), "The input parameter must allow bit operations"); return x != 0 && (x & (x - 1)) == 0; } In this example the error would be localized to the macro, which perhaps isn't what we want, even if the compiler is kind enough to tell us where the macro was included.
- quic5 3y agoA good api (like std.debug.print for example) can implement nice compile errors that fail early with readable messages manually at comptime. Still not as convenient as traits though
- ben20440 3y agoProblems of C: - memory safety - concurrency How Zig addresses them: - it doesn't.
- stefncb 3y agoZig has async and await and is safe from buffer overflows and related memory issues. What it indeed doesn't solve is memory safety with time (so use after free and leaks), though this is somewhat ameliorated by the encouraged programming style.
- apatheticonion 3y agoFor me as a high level application developer, I liked C but felt that it needed a rational module system, generics and structural types. The addition of methods on structs would be nice too (no need for inheritance). The only reason I thought about it is because I have been trying (and failing) to contribute to open source Linux projects - which are practically all written in C and the C++ projects are impossible to read.
- staunton 3y agoC++ has all the things you "felt that C needed". So there must be something more that you dislike.
- lmm 3y agoThe problem with C++ isn't too few features, it's too many.
- staunton 3y agoAgreed. However, my point about GP stands.
- Pannoniae 3y agoNot just too many. But inconsistent ones, hard to use ones and badly designed ones. std::unordered_map anyone? The iterator-based APIs are just cumbersome and error-prone to use, etc... it's really easy to shoot yourself in the foot by forgetting to explicitly implement specific constructors, or by passing the wrong iterator (like begin() vs end()) to an STL template. The C# specification isn't significantly smaller than the C++ one (can't check the exact length right now) but the language is still much easier to understand, even if it has really hairy corner-cases like the difference between readonly fields and normal fields' generated IL or the logic for defensive copies. Most programmers don't have to care, and even if you do, it's not a nightmare to test or experiment with. In C++, I kind of feel I'm trying to squeeze water out of a stone. And even with the huge standard library, basic things are missing from it like a string startswith.
- rkagerer 3y agoThe ≠ prettification of != threw me for a second. I was like, "Does Zig use special characters for operators"?
- thayne 3y ago> Nothing allocates on the heap, without you knowing it and letting it happen. Zig utilizes the Allocator type to achieve this. Any function that allocates on heap receives an Allocator as parameter. Anything that doesn't do so won't allocate on heap, guaranteed. That might be true for the standard library, but it is definitely possible for a function to use an allocator from a struct or a global. Not to mention calling a c function that allocates. > Safety tools to avoid memory leaks e.g. std.heap.GeneralPurposeAllocator Maybe I'm missing something, but AFAICT, that doesn't prevent memory leaks, it just has the ability to log if there were leaks. Like a built-in valgrind. > [Zig] helps your remain safe and avoid leaks So it talks a little about how to _identify_ memory leaks, at runtime. Which is also possible in C with tools like valgrind. But it doesn't mention defer, which is an advantage zig has over c (at least portable c) for memory management. And it doesn't talk about the "safe" part at all, which I would take to mean protections against use-after-free, double-free, unitialized variables, invalid free, etc.
- justincredible 3y ago[dead]