12 ms·
Looking into Zig
- Zababa 5y ago> This year, I decided to look into Zig. Both Zig and Rust are more or less in the same space, replacing C. Maybe we should think about this in terms of space occupied by the languages and not the language themselves. One of the features of C is that pretty much anything can interface with it. Another is that you can write very fast code with it. Another is that pretty much any platform has a C compiler. Another is that it's small and fits in your head. Depending on what you're trying to do, you might use one "C replacement" or another. Another way of looking at it would be to see the shortcomings of C you're trying to overcome. I think this way of thinking would be more productive than just thinking about "replacing C", "replacing C++" or things like that.
- xhealthgothx 5y agoZig libraries can be exported with the C ABI[0], so pretty much anything can interface with it. You can write very fast code with it. It is also small and fits in your head. The only thing missing is a variety of implementations, however there is a clear path to bootstrapping the compiler from source with a semi-recent C++ compiler[1] and from there, there's already a growing number of supported targets[2]. There is also a major focus on making sure cross-compilation is well supported out of the box, including C code that Zig can also compile[3]. It's perfectly fair to compare Zig to C in this case, as the project originated as a dialect of C before the author decided to do a complete clean break[4]. The primary frame of reference when designing the language was C and addressing its shortcomings, both language and tooling. [0]: https://ziglang.org/learn/overview/#export-functions-variables-and-types-for-c-code-to-depend-on https://ziglang.org/learn/overview/#export-functions-variabl... [1]: https://github.com/ziglang/zig-bootstrap https://github.com/ziglang/zig-bootstrap [2]: https://ziglang.org/download/0.8.0/release-notes.html#Support-Table https://ziglang.org/download/0.8.0/release-notes.html#Suppor... [3]: https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace... [4]: https://www.youtube.com/watch?v=Gv2I7qTux7g https://www.youtube.com/watch?v=Gv2I7qTux7g
- Zababa 5y agoI'll restate my point: sometimes when replacing C people want something that's just as fast but also way more safe, and in that case will usually reach out for Rust. That's what the people doing the cryptography in Python did. Rust can also expose a C ABI, you can write code as fast as C. People will often choose Rust instead of Zig because Rust has stronger guarantees about memory safety, and these guarantees are more important for them than having a language that's small and fits in your head. This is why I think talking about what you're trying to achieve is more fruitful than just talking about the language you want to replace. For example, if you want to replace C code and you don't need all of the performance in the world, you couold simply switch to a garbage collected language. In that case, since speed wasn't the most important requirement, a language that isn't usually thought as a C replacement can replace C code. If you want to make the point that Zig is "closer to C" compared to other language that wants to replace C, that's a fair claim to make.
- xhealthgothx 5y agoRust is also not 'small and fits in your head', so that's yet another point it is missing in your initial set of criteria. Its main implementation and library also emits rather large binaries in the usual case. You may not find this important, but others do.
- Zababa 5y agoI addressed that point: > People will often choose Rust instead of Zig because Rust has stronger guarantees about memory safety, and these guarantees are more important for them than having a language that's small and fits in your head. For some people, having a language that's small and fits in your head is not a requirement, not a priority, and sometimes not even important.
- nicoburns 5y agoAs others have pointed out, neither is C if you take into account avoiding UB, the preprocessor, the build systems, the many stdlib functions you need to avoid, etc. You might already know how these work, but that doesn't mean there's less to learn than a language like Rust.
- oddity 5y agoAbsolutely! A common example I like to use is Java. It might be hard to imagine now, but there was a time where Java (and then C#) were pitched as a replacements to C++ (even the name "C#" supposedly derived from "C++++"). There was no reason to write native code any more, many people said. And, they were kind of right. Many people were writing applications in C++ because they thought they had to, even though they weren't writing C++ in the style needed to make proper use of it. A lot of old C++ code bases today look like much more verbose Java applets. This peeling, when it happens, is usually good for everyone since languages (and tools) evolve to better fit the domains in which they are most used and happens so gradually that few notice. The inverse also happens, but with more conflict: you can see this today in Python (and other dynamic languages) adopting typing features aimed at assisting tooling for large code bases, well beyond the traditional learning or glue scripting mentality for which they were designed. This often results fierce debates around language features (like static typing vs dynamic typing) that are really proxy battles for the usage intent of the language. Unfortunately, I think the "replacing X language" mentality happens because it's the easiest point to start with. The field of programming and computers is so vast now that most (all?) people have a very narrow view and it's easy to assume it's universal. I've lost track of all the "things all programmers should read/know/do" articles.
- Zababa 5y ago> Unfortunately, I think the "replacing X language" mentality happens because it's the easiest point to start with. The field of programming and computers is so vast now that most (all?) people have a very narrow view and assume it's universal. I've lost track of all the "things all programmers should read/know/do" articles. I think another effect is that if you really like a language, promoting it is one of the best thing you can do to work with it. When you see the recent (2010 and later) new languages, they all have a story of addressing a particular pain point. Typescript is Javascript at scale, Kotlin is old Java on android, Go is web services, Rust is performance and safety. This is solid marketing, and is part of how these languages end up dominating their domains. On the other hand, a language that's "X but slighly better everywhere" won't really attract people, since there is already code and jobs in that language. Currently in the Great War Of Replacing C And C++, there are lots of fighters. Some people promote Rust, some others Go, some others D, some others Zig, some others to just keep writing C and C++. Part of the battle is writing code, libraries. Part of the battle is making the language and tooling itself better. And part of the battle is making articles, comments, talking to people and promote your language.
- mypalmike 5y agoI think there's an implicit, often unspoken list of C shortcomings wherever these replacement languages are concerned. Some of the most obvious ones are: Macro hell, transitive include hell, and the ease with which even experienced developers can unwittingly create unsafe code.
- rightbyte 5y ago> Macro hell I can hardly think of any language addon more innocent than Cpp (the preprocessor). Imagine what madness would have emerged if it had proper variable assignment, goto:s or any loop construct?
- cassepipe 5y agoWell Zig addresses all of those
- lerno 5y agoWell, except for Zig has the dubious distinction of actually having more UB than C.
- oconnor663 5y ago> Another is that it's small and fits in your head. I know this is a common view, but I want to highlight some counterexamples: - C programmers need to learn about many different forms of UB: https://blog.regehr.org/archives/1520 https://blog.regehr.org/archives/1520. Most take years to learn these rules. Many never learn them. - Most real world C programs use some nonstandard language extensions on some target platforms. C learners trying to read production code have to navigate these. - The C compilation and linking model is complicated and platform dependent. Compilation is a major stumbling block for students who have to learn Make or CMake at the same time they learn C. There is some truth to the "C is simple" idea, I'll grant that. It's obviously simpler than C++. But I think there's a big effect where we tend to forget how hard it was to learn, because we learned it so long ago. There's also been substantial progress since the 70's on designing simpler programming languages.
- Zababa 5y agoThat's a good point. I personally don't share this view, and it's also not something I look for in a language. But people using C currently seem to be very attached to this, and knowing how to connect to your audiance is important.
- surajs 5y agoA reduced feature/keyword set does not a simple language make. C is smaller, but definitely not simpler. Also definition of simplicity is context dependent, I would say in many scenarios C++ turns out to be the simpler choice.
- alfiedotwtf 5y agoThanks! This is probably the first time I've heard C called not a small language. Perl was my main language for over a decade, and when I wanted to go lower, I honestly struggled... but not with the language itself, with the peripherals which are exactly what you described!
- SAI_Peregrinus 5y agoC is small, but it's not simple or easy to get right.
- 0-_-0 5y agoNim compiles to C, so it can be a drop in replacement.
- Zababa 5y agoI think you should be a bit more precise on exactly when. If it compiles to C, that means that it inherits from C the great platform support. I don't know much about it, so I can't judge Nim on other qualities that people attribute to C: you can call it from pretty much any language, it's seen as simple, you can write highly performant code in it, you can use it in very restricted environments, and I'm probably forgetting some others.
- jasfi 5y agoIt's also very easy to write Nim, the syntax has been compared to Python.
- Zababa 5y agoHonestly, I don't think these kind of drive-by shallow comments are selling anyone on Nim. Many people here have posted long and insightful comments about the complexities of replacing C and some other topics, and two of the three comments about Nim are a bit shallow.
- 0-_-0 5y agoI'm on my phone, otherwise I would have written more
- auxym 5y agoI think the fact that Nim compiles to C is neat in that you can use it on any platform that has a C compiler. Here is a recent project that uses nim for AVR platforms, for example: https://github.com/PMunch/badger https://github.com/PMunch/badger
- pjmlp 5y agoI have three main issues with Zig: - @ everywhere, for that I would keep using Objective-C - Compiled languages shouldn't be using text file imports as modules - I already have C and C++ for use after free/move errors Free to disagree, it just means I am not part of the target audience.
- papaf 5y ago@ everywhere, for that I would keep using Objective-C If you are triggered by the AT character, I recommend that you stay away from Slack and Microsoft Teams.
- pjmlp 5y agoI really try hard to, but there are these things called job and customers.
- defen 5y ago> - Compiled languages shouldn't be using text file imports as modules Zig doesn't use C-style textual includes, if that's what you mean.
- pjmlp 5y agoSure it does, @import expects a text file that is then parsed into a struct like data structure. I expect a proper binary module, with native code and symbol table.
- flohofwoe 5y agoThe important part is that (unlike C/C++ headers) included modules can't be polluted with code that's coming before the @import(). Everything else is just an implementation detail. @import also has a second mode where the string isn't a Zig source file path, but a package name. AFAIK currently this also just resolves to a package-root zig file, but it could just as well be extended to some sort of binary format, the question is just: why?
- danielparks 5y agoHe posted a follow up about error handling in Zig that I thought was interesting: https://ayende.com/blog/194466-A/looking-into-odin-and-zig-my-notes?key=42d23515614840bead91eca65823b9d5 https://ayende.com/blog/194466-A/looking-into-odin-and-zig-m... I’ve seen the “clean up errors yourself” argument before, but, like the author, I don’t think it holds water. Often the correct response to errors is to panic() or pass it up the stack so the caller can deal with it or—more likely—panic() itself.
- Zababa 5y agoI think it's nice to have two mechanisms for errors like in ML: sum types for errors that the calling function can realistically recover from, and exceptions for the rest.
- dnautics 5y agoYou can do that in zig with @panic and even convert from sum type error to an irrecoverable exception: `catch @panic("oops");`
- Zababa 5y agoThat's nice. Are the sum types checked by the compiler to ensure that you didn't miss any case?
- squeek502 5y agoIf I understand you correctly, then yes. fn canError(foo: bool) !void { if (foo) { return error.Foo; } else { return error.Bar; } } test "canError" { canError(true) catch |e| switch (e) { error.Foo => @panic("foo!"), }; } Running `zig test` on that file would give: ./tmp/errors.zig:10:30: error: error.Bar not handled in switch canError(true) catch |e| switch (e) { ^
- 5y ago
- Groxx 5y agoPurely on the topic of `errdefer`, I believe the equivalent in Go would be: // must use named returns so you can observe the returned error func yourfunc() (result thingtype, err error) { thing := constructIt() defer func(){ if err != nil { thing.Close() } }() // use thing to do a few things that could error err = thing.setup() if err != nil { return nil, err } other, err := thing.other() if err != nil { return // naked returns are fine too } // etc return } Which is...... technically possible, and not even all that bad (as much as I strongly dislike named returns, since I find them very error-prone), but the fact that I have not ever seen this done does sorta reveal how much friction there is. Instead, I usually see manual additions of err-cleanup to relevant branches, and I've seen that pattern fail repeatedly as well (especially when the code is modified much later by some other person). The Zig equivalent seems like it would be roughly (I don't know Zig) func yourfunc() thingtype { thing := constructIt() errdefer thing.Close() // use thing to do a few things that could error try thing.setup() other = try thing.other() return thing } which is quite a bit more palatable and future-mistake-resistant. A sizable chunk of that is due to `try` of course, but still.
- slimsag 5y agoActually, not only are you right about your errdefer Go implementation - but that's actually fairly common in Go code. Especially for tracing/logging of errors returned from functions; or for e.g. rolling back DB transactions in the event of an error. Here's some ~100 examples from my work's (Sourcegraph's) codebase[0] [0] https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+defer+func%28%29+%7B%5Cn.*if+err+%21%3D+nil+count:all&patternType=regexp https://sourcegraph.com/search?q=context:global+repo:%5Egith...
- mjbrusso 5y agoThe equivalent in Visul Basic would be: On Error Goto line
- tored 5y ago
- koeng 5y agoOne thing that I’m quite bummed out with Zig is the lack of a good http server/client implementation in the standard library. Golang’s std implementation on this is really quite nice.
- throwaway17_17 5y agoDo you feel like http server/client is ‘mandatory’ in a standard library in 2021? I don’t really do any web adjacent programming so it’s not something I ever think about. However, I know I am in a very small group of people who don’t have to worry about web stuff. I’ve been toying with a release of the personal language I use and was just rolling around the idea of what to put in the ‘standard’ library if I do release it. Anything else you think has to be in a std library?
- lostcolony 5y agoIt's very much a nice to have for a new language nowadays, as even if web dev isn't the key niche of the language, any success the language has is likely to still include a need for it. Adding it also creates interfaces and conventions that any third party ones can choose to adopt, to ensure it's a drop in replacement (and even if they don't fully, they'll likely still follow the same paradigm). This is really beneficial as, while the stdlib's version may be very middling, it ensures adopting the language and a third party's http implementation isn't going to trap you into something that becomes unmaintained, and which doesn't have a clean migration path away from; you can always fall back to using the stdlib's http library, since there's an expectation it will be maintained along with the language. That said, if you're putting your language up as a "see the neat thing I did that's helpful in this very specific domain", and not general purpose, with the intent of supporting, it's probably immaterial to you.
- dnautics 5y agoTo some degree if you really needed the client, it would be almost trivial to import libcurl "as it is" into zig.
- 5y ago
- pharrington 5y agoI completely do not understand why the author thinks Rust is a C replacement. I can understand Rust as a C++ replacement. If he was looking for a better C, Zig definitely fits the bill.
- richardanaya 5y agoProbably has something to do with Rust being used in embedded and kernel operating systems due to it's high level of memory control and zero runtime capabilities.
- Zababa 5y agoRust is already used as a C replacement in some cases. However, as I said (https://news.ycombinator.com/item?id=28094998 https://news.ycombinator.com/item?id=28094998), just talking about a C replacement without mentionning why you were using C in the first place and what you value in a replacement isn't really productive.
- pjmlp 5y agoRust needs decades to catch up with domains that C++ has managed to keep for itself, like GPGPU, GUI composition engines, machine learning, HPC. The other domains where C++ was strong for general purpose applications have been taken over by managed compiled languages like Java and .NET, which anyway make use of C++ libraries alongside their AOT/JIT compiled stacks. There is little value to add from Rust in such scenarios, e.g. it won't make my WinUI application, CUDA shader or weather simulation model, any more safer than they already are.
- andi999 5y agoC++ for gpgpu was more of an afterthought in nvcc, wasn't it. I would be surprised if not most cuda kernels are written in plain C.
- pjmlp 5y agoNot at all, it was one of the reasons why OpenCL lost, by staying too long focused on C. CUDA supports C++ since version 3.0, and NVidia designs the cards to follow C++ semantics, regardless of the polyglot capabilities from PTX. Hardly an afterthought, it was Khronos that became surprised that most researchers don't want to use plain old C on their research efforts.
- richardanaya 5y agoEvery time I read about Zig, I feel like I see a nice language that's only an incremental improvement to C mostly in terms of ergonomics. It's hard for me to imagine people giving up C for only incremental gain.
- defen 5y agoAn alternative perspective is that you don't have to give up C to use Zig - they are extraordinarily easy to mix together in one project. So you can incrementally move an existing project to Zig without rewriting the world.
- naasking 5y agoCompile type computations, trivial cross-platform building and linking, vastly better error handling, and more. Honestly, I'm having a hard time seeing how these are only "incremental" improvements over C.
- sharikone 5y agoYou could argue the opposite and say that because it is incremental and easy to mix with C, it will be more easily used. When I program on my own I often write an idea in C and then convert it to C++ when complexity rises, precisely because C++, in terms of syntax, is an incremental update to C. Using say Rust would entail a more serious rewrite. Unfortunately Zig and Rust are quite different in syntax. I would love to change my workflow from C->C++ to Zig->Rust but the transition is not as nice.
- flohofwoe 5y agoI thought that too in the beginning, but the comptime features, type reflection and error handling really go beyond C, while still being a simple and elegant language (in a way even simpler than C). Also Zig is a nicer C/C++/ObjC toolchain than most C/C++ toolchains (because of simple cross-compilation and integrated build system).
- hansvm 5y agoI don't know how many people will switch, but I think it's worth it for even a few of the new features. - You can get cross-compilation for free in Cython projects just by switching compilers. - (nearly) Arbitrary width integer types are a godsend for the right kinds of bit twiddly projects. - The error handling is actually really nice.
- ngrilly 5y agoI really like Zig, but it's difficult to compare it with Rust without mentioning Rust memory safety and data race freedom.
- philprx 5y agoThanks a lot, TIL this about zig: https://scattered-thoughts.net/writing/how-safe-is-zig/ https://scattered-thoughts.net/writing/how-safe-is-zig/
- tored 5y agoGreat article. I mostly do PHP, PHP has support for FFI and I have some private projects where I call C libraries directly over FFI. I have been thinking of using zig to implement my own C libraries when needed. Zig looks like a more sane approach than using C, another alternative would be D.
- yakubin 5y agoZig looks overall interesting to me, but I don't like that it reproduces one of the pains of C++ templates, i.e. their dynamic typing. What I mean is, C++ templates aren't checked before being executed, and instead you get compilation errors (at c++ templates runtime) which mangle the use and definition of templates, e.g. template<typename T> concept HasId = requires (T a) { T::kId; // "the expression T::kId is a valid expression that will compile" }; // A and B are expected to be structs, where each has an associated integer constant kId and a field "int value". template <HasId A, HasId B> int min_id(const A a, const B b) { if (A::kId < B::kIs) { return a.value; } else { return b.value; } } struct A0 { static constexpr int kId = 1; int value; }; struct A1 { static constexpr int kId = 2; int value; }; // etc. struct A180 { static constexpr int kId = 2; int value; }; int main() { (void)min_id(A0(), A1()); (void)min_id(A1(), A2()); // etc. ending with: (void)min_id(A179(), A180()); } And now you get 180 errors about your typo (if not for max error number limits in the compiler), when really there is only one error in the definition of min_id. I used concepts to show that in the long tradition of C++, they're an additional feature that doesn't really help (quantity over quality of features). Haskell solves that with typeclasses, Rust solves that with traits. What's really important about these solutions is that when you write a function generic over a trait or a typeclass, you are limited to using what is defined in the trait/typeclass. The trait/typeclass function both as documentation and as something that gets you better compilation errors. "typename" in C++ is basically "any", but for values of template execution. Typeclasses/traits on the other hand are a sort of types for those values (which we normally call "types"). And similarly, in Zig there is an "any" type for types, just like in C++ --- it's called "type", and AFAIK there is no way to constrain your types with types of types.
- chakkepolja 5y agoIs the problem about C++ templates it's error messages or not being able to strictly type the arguments in type signature? I have a question to the compiler / IDE people here. In case of C++ templates, is it possible to output one error like: eg "class UserData needs to implement operator< to be used in min() function" instead of 200 lines errors? I mean, complex cases it's not possible but at least for simple ones like using an iterator variable directly instead of dereferencing it?
- sintezcs 5y agoI highly recommend the last episode of the “Corecursive” podcast. The guest of the episode is Zig creator - Andrew Kelly, and he tells about the history of Zig, it’s really interesting! https://corecursive.com/067-zig-with-andrew-kelley/ https://corecursive.com/067-zig-with-andrew-kelley/
- agbell 5y agoI know very little Zig, but I agree with the post, that comptime seems simple, obvious and powerful. It is interesting that it bears some surface level similarities to type constructors in languages with higher kinded types.
- naasking 5y agoYes, and in principle you can generalize the type language into a full language with lambdas and such (type constructor is a lambda(ground type)->ground type), though you typically want it to be strongly normalizing so you know that the compiler will eventually halt. Then you don't need kinds, kind polymorphism, sorts, etc., you just have a dependently typed language with types and universes.
- agbell 5y agoYeah, functions returning types based on computations in Zig can look a lot like Idris if you squint
- Royi 5y agoIf only Zig had Multi Dispatch and built in support for arrays (Vector, Matrix, Tensor).