20 ms·
Assorted Thoughts on Zig and Rust
- the_mitsuhiko 6y agoI quite honestly wonder sometimes why Rust excited me so much when I first started using it, but I do not get such excitement from Zig. Nowadays it's very easy to be excited about Rust because it demonstrated that ownership works but when I started using it, there was still garbage collection etc. Zig looks really cool but it feels like it has a high chance of being a niche language. Rust never felt like that.
- jventura 6y agoMaybe it's because Rust is backed by Mozilla, which is a bigger player than Zig's creator?
- the_mitsuhiko 6y agoThat if anything would have made me more suspicious. Mozilla had and still has an awful track record of keeping projects alive.
- deleted 6y ago[deleted]
- ChrisSD 6y agoRust is currently backed more by Amazon and Microsoft than it is by Mozilla. The main contribution of Mozilla is to hold the trademark on behalf of the Rust project.
- weinzierl 6y agoWhile Zig is an awesome project I feel a little bit the same. My two cents are that it is because Zig, as innovative as it is, has nothing that other languages couldn't copy or assimilate. With Rust it is a different story, because the ownership model cannot be plugged into other languages without changing them fundamentally. My prediction is similar to yours that Zig will be an important research project but not go mainstream. A counterargument is of course that Zig's closeness to C will make it win in conservative industries and it wouldn't be the first time a more incremental approach wins over the more innovative one. Also I have to stress that I only have a cursory knowledge of Zig so far. I hope I didn't do the language injustice and I'd like to hear other opinions about this.
- kasperni 6y ago> has nothing that other languages couldn't copy or assimilate. You cannot "copy" simplicity into a complex language. (Not trying to start a discussions about whether or not Rust is complicated.)
- the_mitsuhiko 6y agoAgreed. One thing though is if a language is simple because it's early or if simplicity is a core value to the language development. A lot of "simple" languages just become complex over time as they get more users and feature requests.
- flohofwoe 6y agoRust and Zig target different audiences though. It's often been said before, but Rust is mainly a C++ replacement, while Zig is mainly a C replacement. I have switched from C++ as my default-language to C a few years ago, and having dabbled a bit in both, I feel a lot more attracted to Zig than Rust for future projects (and mostly for the same reasons why I switched from C++ to C). If Zig can settle in the niche that C is used for today (including "nearby" areas where C programmers consider switching to a higher-level language), then it is already a great success. I think (rather: hope) what we will see in the future is that no single language will dominate certain fields anymore like it was the case in the 90's and early 00's. Rust doesn't need to fit into every niche, and it would be harmful to bend Rust in a way that it fits everywhere. It would end up as a "kitchen-sink language" with tons of competing concepts and ideas. This is exactly what's currently killing C++.
- twic 6y ago> If Zig can settle in the niche that C is used for today That niche is "code that needs to be portable to any system with a C compiler", or "... to a particular system that only has a C compiler" so i am not sure it can. There are lots of projects using C that aren't in that niche, but i don't believe there is a good reason for any of them to be using C over C++ these days. It's either history, inertia, or Luddism.
- lifthrasiir 6y agoIt might be the timing that matters. The very needs for safe and friendly system programming arose---or rather became clear---around 2010, when both Rust (0.1) and Go appeared. Having appeared 5 years later, Zig can be seen as a follower (a very great one, of course) and may no longer give much excitement as Rust did.
- j-krieger 6y agoFor me personally, Rust was a challenge. Most programming languages are more or less the same. But Rust forced me to think in a way I've never encountered before. I guess this is also why many people like it.
- creata 6y agoFor me, I wouldn't say it's just because it was a challenge. The ideas that Rust makes explicit — borrowing, tracking lifetimes, mutable vs. immutable references — they're lessons that carry over into other languages very well.
- gmueckl 6y agoNever underestimate the importance of aggressive marketing and sales around tools for software developers. Even OSS projects need clever sales pitches and visibility to become successful. Mozilla had the resources to pull that off for Rust.
- layoutIfNeeded 6y agoIn my experience software developers getting excited about their tools rarely lead to good outcomes for the projects they're working on.
- snovv_crash 6y agoSame. Make the tools boring so the excitement can go into the product.
- kazagistar 6y agoFor me its easy: haskell was cool for more obvious reasons but a bit much, and rust felt like an interesting yet practical very strongly typed language.
- jahaja 6y agoI think it's much about preference, some like simpler languages, some like more expressive ones. I for one have a really hard time liking expressive ones, hence Go & Zig is my small but high-quality toolbox rather than a larger toolbox. I just can't get that excited about the language itself beyond a certain point, I just want simple, predictable high-quality tools to help me produce simple, high-quality code and applications. Anyway, all of the above is ofc subjective.
- pron 6y agoMy impression was completely the opposite. I was very excited about Rust at first, especially the ownership system, but after a while I saw that it was a cleaned-up C++ with most of C++'s problems (one of the most complex programming languages in software history; very slow feedback loop). Zig, on the other hand, seems revolutionary and a complete rethinking, from the ground-up, of what low-level programming should be. True, Rust is probably 100x more popular, but is still well below 1% market adoption, so we're comparing two rounding errors in terms of adoption.
- the_mitsuhiko 6y agoRust is trying to replace C++. If you were not in the market for C++ to begin with it's unlikely that Rust is going to excite you.
- pron 6y agoThat's one way of looking at it, but that I'm shopping for a C++ replacement doesn't mean I'll necessarily like another C++-like language. C++ is my primary language, and if I replace it -- given that switching a new language is very costly no matter which new language I pick -- I might as well wait for a more revolutionary replacement, that fixes all/most problems I have with it, rather than just a few. I don't know if Zig is that thing just yet, but it looks more promising -- to me, given my particular taste -- than Rust. But, TBF, I was very excited about Rust at first, so my excitement for Zig might wane as well.
- mlindner 6y agoRust is not "trying" to replace anything. Rust can and is used plenty for embedded programming where only C was ever used previously. People have commented they prefer writing simple command line applications in it more than they do python.
- Rusky 6y agoI really don't see how Rust has most of C++'s problems. Slow compiler, sure, though things are changing. Most complex languages in history? Only if you squint at it from 10000 feet and ignore what the complexity is doing. C++ has approximately eighteen different partially-overlapping categories of variable initialization, many of which are legacy-but-still-used! [0] And some of those categories have changed their boundaries every language version since C++11 (based on the definition of "aggregate"). C++ has three to five partially-overlapping kinds of type inference all in active use (auto, decltype(id), decltype((expr)), decltype(auto), template argument deduction)! This is interleaved with name lookup and overload resolution (below), so if some subexpression isn't compiling how you expect, you have a vast space of language features potentially to blame. C++ has so many kinds of name lookup and namespacing that I'm not even sure how to count them. There's unqualified lookup, argument-dependent lookup, qualified lookup, class member access, etc. Sometimes you can't refer to things defined later (outside a class) and sometimes you can (inside a class). There is even undefined behavior if you mess up namespacing! (Undiagnosed ODR violations are every experienced C++ programmer's nightmare.) C++ has ad-hoc overload resolution based on un-scoped identifiers, which even crosses namespace boundaries using one of the above name lookup modes. It has two kinds of user-defined implicit conversions ("explicit" and implicit) that also affect this selection process, on top of the zoo of "type promotions" inherited from C. C++ classes have five to six kinds of "special member functions," some of which may be defined automatically by the compiler, each with its own rules for when and how, based on what else is defined in the class. These also contribute to overload resolution, of course. [0]: https://blog.tartanllama.xyz/initialization-is-bonkers/ https://blog.tartanllama.xyz/initialization-is-bonkers/ Rust has a complexity of its own, but it's quite different in scale and quality. There is exactly one way to initialize a variable, exactly one kind of type inference, only two ways for names to be resolved (directly or via an imported trait). Overloading, implicit conversions (of which there is only one kind, Deref), and the replacement for "special member functions" (Copy, Clone, Drop) are all based on exactly one mechanism (again, traits). The article has a pretty accurate description of how people experience Rust's remaining C++-like complexity, IMO: "I don't remember the order in which methods are resolved during autoderefencing, or how module visibility works, or how the type system determines if one impl might overlap another or be an orphan." But one important aspect it leaves out (not being a C++ article) is that if you mess up any of these, you just get a compiler error- and Rust is well-known for having extremely helpful error messages. In C++ you may get a compiler error (known for being extremely unhelpful) or you may get undefined behavior. Zig is certainly a smaller language than either C++ or Rust, but that comes at a cost. I would much rather hear discussion of those actual trade-offs than yet another "Rust is just as complicated as C++" non-claim.
- gridlockd 6y ago> I quite honestly wonder sometimes why Rust excited me so much when I first started using it, but I do not get such excitement from Zig. Good! Maybe you can actually get something done using it then, instead of getting lost in mastering lots of concepts just for the sake of it.
- j-krieger 6y agoI enjoy Rust and have been writing it for quite some time. However, I feel like server side languages still have a long way to go. Client-side languages in comparison have been only growing better (one could make a lot of negative points about TypeScript and node in general, but the ecosystem is a joy to work with). I feel like this is because people have accepted C and C++ with their respective pain points for close to 50 years now, because there really were no viable contenders. I like how this is starting to change. I still feel Rust, Nim and Zig kind of get the right ideas, but they are not there just yet. I like rust with it's expliciteness and correctness, but I wish some stricter features were opt-in. I do not feel that the borrow checker is the ultimate solution to safety problems. Its strictness can make working with Rust a pain. As the author demonstrates, sometimes you know what you want to do and how to write it in other languages, but it can take quite a while to get it down in such a way that the rust compiler will accept it. I think enabling a language to be garbage collected in general, while making a borrow checker opt in for special, time critical functions is the best of both worlds. I also think Rust may focus a bit too much on the terseness of its syntax. This can make modern Rust hard to read because there are so many special tokens.
- peteretep 6y ago> node ... the ecosystem is a joy to work with I'm not entirely sure I've heard anyone express this before. What do you like about it?
- renke1 6y agoTools like ESLint or prettier are really top-notch. TypeScript itself is a really nice language, too. Its type system is pretty powerful, more than one would expect certainly. Going from TypeScript to Kotlin feels like such a downgrade in that regard. Also the wider JS ecosystem really has some great, high-quality projects that explore (or possibly reinvent, in a positive way) ways to do things in an elegant way.
- j-krieger 6y agoMultiple points: I really like how there's a package for everything. Many other languages have adopted this method, but for example in Rust, most packages are not as mature as JS packages are. The JS ecosystem is responsible for spawning services that fund Open-Source developers. Packages are also easy to install. I spent a whole weekend trying to install Postgres and Drogon (a http server) on C++ with conan/vcpkg, and in the end I could only manage with a docker container installing these dependencies via apt-get, which was exactly what I did not want. Furthermore, NPM is the package manager among package managers. No other package manager comes close. Python has too many options, virtual environments and so on. Rust's cargo is good enough, but I really do not enjoy having to install a seperate package (cargo-edit) just to add a package via the command line instead of editing text files. C++'s package management systems are most of the time a total letdown or don't have widespread adoption. Not only that, npm also takes care of a package maintainer needs (semver, transitive dependencies, etc) --- Many people express dissatisfaction about build tools and the like, but as someone who got into webdev at the exact time people began building larger applications on the client, I love them. Sure, when they first appeared they were a pain to work with, but most modern build systems are amazing. I can just include most files (MD, SVG, images) and work with as if they were JSON/JS files and don't have to worry about how it's done internally. --- Prototyping is really fast and important if you work with startups or want to create a proof of concept for a customer. I can throw together a functioning backend with http server and database in a couple of days. Modern JS frameworks are uncomplicated and can be minimal if you know how to use them. For example, my personal site (https://juliankrieger.dev/ https://juliankrieger.dev/) is written in Gatsby and React.js, but it weighs only 20kb. Now, there's not much on it but all content is rendered server side and only rehydrated when I need it. For anyone interested in getting even better results on a personal homepage, I recommend looking at 11ty for a static site generator and htm/preact for an absolutely minimal React implementation that only ships javascript where you really need it. --- I also like how it enables me to write scripts for personal use and at the same time I can use Node for larger projects. I've used python for this in the past, but a large python code base can be a beast of its own. Moreover, not having to recompile dependencies on a code change is a welcome feature. My main problem with Rust are the large compile times. Node even enabled me to hot reload code under the right conditions, making iterative development an insanely fast process.
- lambda_dn 6y agoI think Zig's biggest advantage is that it's just C without the warts, or footguns as is said in the Zig world. The comptime feature is probably the most exciting thing I've seen in a while. I've looked at Rust and feel it's more of a competitor to C++, Java and C#. Whereas Zig is C done right.
- deleted 6y ago[deleted]
- johnisgood 6y ago> The comptime feature is probably the most exciting thing I've seen in a while Just please do not make the mistake of believing that it is unique to Zig. Factor brings the best of Forth and Lisp together, so meta-programming or extending the language is possible quite easily, for example. You could extend the syntax or add constructs pretty easily, and so forth. Anyways, an example can be found here: https://rosettacode.org/wiki/Compile-time_calculation#Factor https://rosettacode.org/wiki/Compile-time_calculation#Factor but this barely scratches the surface. It does not mention `<< ... >>` which evaluates some code at parse time. You can execute code before the words in a source file are compiled. https://docs.factorcode.org/content/article-literals.html https://docs.factorcode.org/content/article-literals.html https://docs.factorcode.org/content/article-syntax-literals.html https://docs.factorcode.org/content/article-syntax-literals.... https://docs.factorcode.org/content/article-syntax-immediate.html https://docs.factorcode.org/content/article-syntax-immediate... https://docs.factorcode.org/content/word-flags{,literals.html https://docs.factorcode.org/content/word-flags{,literals.htm... I remember when I did something like: SYMBOL: aligned-16-char << : 16-byte-alignment ( c-type -- c-type ) 16 >>align 16 >>align-first ; char lookup-c-type clone 16-byte-alignment \ aligned-16-char typedef >> when I was working on some binding. You could use it in a struct like: STRUCT: foo { bar aligned-16-char[16] } ; Or something like this is pretty typical (when writing bindings/ffi): << "libotr" { { [ os windows? ] [ "libotr.dll" ] } { [ os macosx? ] [ "libotr.dylib" ] } { [ os unix? ] [ "libotr.so" ] } } cond cdecl add-library >> Those are just some examples, but it is pretty powerful. It supports (and encourages) interactive development. Profiling and debugging is a breeze and highly detailed and useful, you can easily disassemble words (functions), you can get a list of how many times malloc has been called in some circumstances, there is runtime code reloading (a vocabulary that implements automatic reloading of changed source files[1]), and so on. And on top of all this, you can compile your stuff to an executable that is less than 4 MB! And of course you do not have to do stack shuffling at all, you can easily use locals which is useful for math equations and whatnot. Plus did you know that the Factor compiler supports advanced compiler optimizations that take advantage of the type information it can glean from source code? The typed vocabulary (yes, it is not part of the language, but implemented as a vocab) provides syntax that allows words to provide checked type information about their inputs and outputs and improve the performance of compiled code. I would like to repeat because if this was not the case, I would have never bothered with it: you can create a single executable file that is less than 4 MB of size if you wish so! Of course it encourages interactive development, but still, it is great to have an optimizing compiler that can do all this easily. And mind you, this part is also written in Factor itself and is available as a vocabulary (vocab). [1] There is a vocabulary named io.monitors and loaded source files across all vocabulary roots are monitored for changes. You can read more about it here: https://docs.factorcode.org/content/article-vocabs.refresh.html https://docs.factorcode.org/content/article-vocabs.refresh.h... --- So all in all, I think Factor is great. I was shocked at how modern (and how many) libraries it has, especially considering only a handful of people have been working on it. Slava Pestov created the language, and some people joined him later on. If you want to learn more about it, start here: https://concatenative.org/wiki/view/Factor https://concatenative.org/wiki/view/Factor. There are videos, there are papers, there are lots of resources to get started. :) The language misses a couple of things, but it is being worked on.
- latch 6y agoLooks like there's active work going into it (1) (2), but Zig's lack of built-in http library has been a showstopper for me. I've had a few small side projects I wanted to do which involved exposing an API. (1) https://github.com/ziglang/zig/issues/910 https://github.com/ziglang/zig/issues/910 (2) https://github.com/ziglang/zig/issues/2007 https://github.com/ziglang/zig/issues/2007
- deleted 6y ago[deleted]
- mseri 6y agoI am still torn about this. I can see the usefulness of it from python and go, but I also fear that it brings an unnecessary large maintenance burden. Http standards are evolving and soon the implementation will get stale, being in the standard library there will be need to updated it and possibly maintain backwards compatibility, even when it becomes clear that a new design may be a better option. So, suddenly people will start using new, often better, libraries but you need to keep find workforce to maintain the old stdlib http library. That is mostly why I keep thinking that it would be better to have it as a separate library, with an easy-to-use package manager to discover and use it.
- coldtea 6y ago>I am still torn about this. I can see the usefulness of it from python and go, but I also fear that it brings an unnecessary large maintenance burden. Http standards are evolving and soon the implementation will get stale, Why would it "get stale"? It doesn't get stale in Golang. If anything, being part of the standard library is a greater assurance for more eyes going into it, and not having it get stale, as opposed to the language having 5-6 half-abandoned third party libs...
- flohofwoe 6y agoKeep in mind that a HTTP server is only useful for a subset of users, it might be an "obvious" requirement for you, but it definitely isn't for me ;) Features like this should go into libraries, but not the standard library. When looking for a Go or Python alternative, Zig doesn't immediately come to mind TBH. Also: Go (or python) has no UI system or 3D API wrapper in the standard library, but those are (probably) useful for at least as many people as a HTTP server. Does that mean that Go should get UI and 3D-rendering support in the standard library?
- IshKebab 6y ago> Both languages will insert implicit casts between primitive types whenever it is safe to do so, and require explicit casts otherwise. I think Rust always requires explicit casts. It's a bit annoying tbh - especially for array indexing - indexing with a u8, u16 or u32 should always be fine but you still have to do `as usize`.
- jamii 6y agoI forgot rust doesn't upcast numbers, but I was mostly thinking about https://doc.rust-lang.org/nomicon/coercions.html https://doc.rust-lang.org/nomicon/coercions.html
- creata 6y agoYou can also write `as _` most of the time. Edit: But as the reply below says, please don't.
- coder543 6y agoWhich is not recommended, because it could silently truncate if _ is a smaller type, or later becomes one as the code is edited over time. Much better to use ::from() or into() for numeric type conversions, which are only implemented for types that are guaranteed to fit the value, unless you know that truncation is perfectly fine behavior in a particular instance... which it rarely actually is.
- creata 6y agoAh, sorry; I forgot that `as` let you cast bigger types to smaller types. Yeah, that does make the tragically verbose `.into()` the safer option, unless you know that the type you're casting into will always be bigger.
- richardwhiuk 6y agoIt's fine for increasing width - e.g. u8 -> u16. There's a lint you can enable to detct truncation - https://rust-lang.github.io/rust-clippy/master/#cast_possible_truncation https://rust-lang.github.io/rust-clippy/master/#cast_possibl...
- pizza234 6y agoI think this an argument like the following is not really meaningful: > Most of this difference is not related to lifetimes. Rust has patterns, traits, dyn, modules, declarative macros, procedural macros, derive, associated types, annotations, cfg, cargo features, turbofish, autoderefencing, deref coercion etc Nobody forces beginners to write macros. Beginners are only macros _users_. With time and experience, the need for macros emerges by itself, then learning them is a natural part of the process. But even then, nobody is forced to write any. Dyn is also a very obvious concept to anybody who knows a bit of OO-programming in lower-level languages (e.g. C++). The choice to make dynamic dispatching explicit is arguable, but ultimately, although making it explicit is (AFAIK) Rust-specific, the concept itself isn't. Complaining on pattern matching? C'mon :-) It's a bit like a Python programmer complaining that Golang has a switch/case. I don't argue that Rust is hard or not, but it seems to me that the author was overwhelmed, and complained about everything, even simple things. In my experience, in the Rust learning process (and programming experience), all the concepts above are dwarfed by the headaches induced by the borrow checker. > Zig has it's own implementation of standard OS APIs which means that linking libc is completely optional. Among other things, this means that zig can generate very small binaries which might give it an edge for wasm where download/startup times matter a lot. I'm curious about the details of this. Rust has `no_std`, however, it seems that in Zig, this is more (in a way) granular?
- k__ 6y agoYou're right, but I find Rusts concepts/keywords also a bit confusing. I know what a class and what an interface is, but Rust doesn't have these. It has a struct, which is kinda like a class without methods? It has a trait, which is kinda like an interface but with implementations for methods? It has ... implementations... which kinda make a struct to a class, but not really. I don't mean to hate here, but these are all things that need to be understood in some kind of way.
- kzrdude 6y agoOnce understood, even taken together, none of those have as many rules baked into one concept as "class" does; each is simple.
- nimmer 6y agoPity that Nim was not part of the article.
- fwsgonzo 6y agoIt's weird because out of all the languages I have made a special environment for, Zig is the only one I couldn't figure out how to. For example, how to write global asm? How to call the Zig main function? I guess I need a trampoline? I have an emulated env with no filesystems and such things, where you just communicate using system calls - so nothing special, however since you can't just use the standard libraries I have to set the environment up myself. That is fine. It's also cross-compiled. For rust I had to add some custom linker flags, use global_asm for the startup code, and then with no_std I could just call my own main function. The only really annoying part of the whole thing is that there is no feature in Rust to force a function to not be removed. I could override the global allocator to make allocations very fast. For C/C++, it's the same as in Rust, except you can use __attribute__((used)) to make a function not get removed. It's also easy to override memory- and string-functions if you have system calls that do these things faster. Overall the C/C++ environment was the fastest. For Nim, I only had to use C++ as a backend, and then call NimMain(), add a few extra flags, and it would just work. I only wish that Nim was more popular.
- IridescentRose 6y agoGlobal Asm: comptime{ asm("nop"); //Your asm here } Calling Main From an arbitrary point: const root = @import("root"); //.... root.main();
- lzutao 6y ago> there is no feature in Rust to force a function to not be removed Can we use `pub` to make it public and not to be removed ?
- fwsgonzo 6y agono I think I tried just about everything: pub, extern etc. The only thing that worked was adding a linker argumment: "-C", "link_args=--undefined=<name_of_function>" That will force the linker to assume it's referenced.
- martinhath 6y agoThis is a really great writeup! I was using Rust as my main programming language from some months before 1.0 up until maybe early 2019. I have only written somewhere in between 100 and 1k lines of Zig, but generally feel that I agree with most of what's being brought up here. Here's a mind dump: > Zig manages to provide many of the same features with a single mechanism - compile-time execution of regular zig code. This comes will all kinds of pros and cons, but one large and important pro is that I already know how to write regular code so it's easy for me to just write down the thing that I want to happen. This is a huge one for me, and I really don't understand why Rust didn't jump on this earlier. Using the programming language for configs, generics, macros, and anything else that you'd want at compile time just seems like such a huge win, instead of having weird preprocessors-like systems, some config file format with arbitrary limitations, and weird marcro-like systems that either have crazy syntax (like `macro_rules!` in Rust), or that are way too limiting (like `const` functions in Rust). Jon Blows language seem to take a similar stance as Zig; let's see if it ever hits public beta. > On the other hand, we can't type-check zig libraries which contain generics. We can only type-check specific uses of those libraries. This is definitely a concern I have about this "C++-like generics". My experience with C++ suggests that this is bad, but on the other hand, improving even just error messages would be such a day-night improvement, that I don't really trust my judgement on this one. > Both languages will insert implicit casts between primitive types whenever it is safe to do so, and require explicit casts otherwise. Is this really true? I seem to recall having to have plenty of `as usize` in my code when using smaller integer types for indices, but maybe this has changed (or maybe I'm misreading what's being said here). > In rust the Send/Sync traits flag types which are safe to move/share across threads. In the absence of unsafe code it should be impossible to cause data races. This is probably Rust's main selling point, because as far as I can tell, no other mainstream language comes even close to getting static thread safe guarantees (up to your definition of mainstream). As time goes on, however, I'm getting less and less excited about this, because most of my programs are not multithreaded, and the very few times that I need multiple threads, there is often very obvious and small boundaries in between the threads. It's just not very interesting to me that I _could_ be writing programs with thousands of threads all jumping around without having to worry about data races, because I don't really worry about it in the first place. But still, I as a Rust programmer, have to pay the price for this option being available. > Undefined behavior in rust is defined here. It's worth noting that breaking the aliasing rules in unsafe rust can cause undefined behavior but these rules are not yet well-defined. I'm not sure what to say about this, except that it's surprising that there seem to be a lack of voices about this in the Rust community. How can anyone comfortably write `unsafe` code without knowing what the rules are? Especially when the compiler is so "good" at depending on the "rules"? I don't understand. I have pretty limited experience with unsafe, but I have written some, and was often confused about which bugs were my logic bugs and which were the compiler assuming I didn't break some rule I didn't know about. Combine this with a poor debugging story overall, and you have a pretty miserable experience programming. Maybe this isn't a problem in practice, or maybe all people succesfully writing `unsafe` code for libraries are also `rustc` veterans? > @import takes a path to a file and turns the whole file into a struct. So modules are just structs. This is a very nice approach! I remember from earlier Rust that the module system was a real pain point for beginners, and can also remember really struggeling with it. Curiously though, I also remember looking back, not understanding why anything was confusing about it. This was also redone(?) at some point, and I think it's nicer now. > In rust my code is littered with use Expr::* and I'm careful to avoid name collisions between different enums that I might want to import in the same functions. In zig I just use anonymous literals everywhere and don't worry about it. I've always been bothered by Rust's inability to infer the `enum` type in a `match`; in other places Rust has no problems being automagick, and this is really very annoying to go around, either with `use Foo::*` before each match, or having it in file scope and hope for no collisions. Zig seems to take exactly the approach I'd go for. > Re allocators I think Zig's stand on explicit allocators is very good; I've seen enough bad code in other languages that allocates here and there for things that, very clearly, doesn't need to be there. Having the language be explicit about allocations makes it easier to stop and say "hey wait a minute, is this realy the way I'm supposed to do it?", but without having to jump through hoops if you _just_ want to allocate something somewhere (define a global allocator yourself). And, as a bonus, it's easier to handle the allocations of other peoples code. > Zig has no syntax for closures. I definitely need to write more Zig to find out whether this is a problem or not. I've written a lot of C++ lately, and while there _are_ closures available, I think I've only used them once. Maybe the reason for my comparatively heavy closure usage in Rust was that so many methods in the standard libray took closures that you're shephearded into making similar methods for your own types. > Zig's error handling model is similar to rust's, but it's errors are an open union type rather than a regular union type like rust's. I really think the error story is why I prefer Zig to Rust now. The giant error `enum` in Rust is definitely what I'd go with because it's simply not feasible to manually track which functions return what errors and making individual enums yourself, even though this is super easy for the compiler to do, like Zig shows. Not to pick on anyone in particular, but sometimes it feels like many programmers think that a program only consist of the happy path and that errors are somehow rare and not worth dealing with properly. Both Rust and Zig are huge helps to combat this mindset, but I do think that Zig comes out ahead, simply by being less annoying to work with. Also, while some people might say that `Result` just being a part of `core` and not a magic special language thing is cool, I do appreciate Zig's usage of `?` since it's way less typing for something that happens _all_ the time. > Zig's compilation is lazy. Only code which is actually reachable needs to typecheck. So if you run zig test --test-filter the_one_test_i_care_about_right_now then only the code used for that test needs to typecheck. I didn't know this, but this is awesome! > Zig has absurdly good support for cross-compiling. I've never understood why cross-compiling isn't an out-of-the-box feature in all languages. Don't you basically just have to target a different instruction set? Well, and a different executable format. But still, compared to all of the other crazy things compilers are doing, this seems very straight forward in comparison. > Zig has an experimental build system where the build graph is assembled by zig code. See above. I really really really don't understand why all languages doesn't do this already. > In rust, blocks are expressions. This is something I really like about Rust and a pattern I've used a lot, where I'd say let some_thing = { let foo = ... let bar = foo.baz() + quiz(); ... foo }; to avoid accidently using `bar` somewhere else. Granted, since Rust allows shadowing this isn't really a problem most of the time, but it's definitely something I miss when writing C++. Zigs version is somewhat verbose, but I'll manage. > There is an in-progress incremental debug compiler for zig that aims for sub-second compile times for large projects. Based on progress so far, this is a plausible goal. Andrew's work on binary patching executables is really cool. I hope we'll get to compile times this low, even for moderately sized projects. > real 23m27.475s This is just sad. Despite all the work the contributors to `rustc` are doing, it just seems that they are in a completely different league with respect to compile times than what I'd like. I hope the steady progess they're making will either make some jumps, or continue for a while :) > Main points so far: This is a great summary, and I think people reading it (or the whole post) will have a pretty good idea of where they stand re. the two languages.
- 02020202 6y agoi found zig when i found jai. i am not interested in non-gc language. there is no type of work that i do that would require non-gc language. but i was recently looking into them, just out of curiosity. but it's horrible. managing my own memory is something that i, as developer, have zero interest in doing. anyway, since rust is fugly af, like the fugliest syntax ever created in the history of human kind, and lacks proepr support for windows and ides, zig looks like i might give it a go once they get http in, since i mostly do networking stuff. also i am still interested in jai.
- pron 6y ago> As long as all unsafe code obeys the aliasing and lifetime rules, rust protects completely against UAF. Zig has little protection. ... now. The goal is to have full protection against UAF (in safe-mode only, of course).
- mwcampbell 6y ago> (in safe-mode only, of course) Then it seems to me that Rust still has an advantage, in that it offers full protection of UAF with zero runtime overhead.
- pron 6y agoThat's like saying that a helicopter clearly has the advantage over a car in that it can fly. True, that's an advantage, but you don't get to just pick the advantages -- they come in a package. That package is better overall only if the advantage comes at no additional cost and has no associated downsides. I, for one, think Rust's advantages comes with a hefty price tag, and can have negative effects even on correctness compared to Zig. However, it's hard to intuit which of the approaches is better for correctness; only time and empirical observation will tell.
- higerordermap 6y agoIf takes twice time to write? Of course, I too believe statically verifying is important for mission critical software. But it comes with a cognitive overhead.
- NationalPark 6y agoIt takes some time to get up to speed with Rust, but I don't think it takes particularly longer to write a correct program in Rust than it does in other languages. I often find myself reaching for Rust rather than Python now, even for small things.
- pron 6y agoThe same is true for C++, and early reaction was similar: hey, we don't need high-level languages anymore! But after a few decades with C++ we've found that the burden manifests not when writing the program but when maintaining a large codebase over many years in a team whose members change over time.
- auggierose 6y agoNone of these languages represent the medium term future, but are certainly interesting milestones along the way that will inform it. The real future though is theorem proving systems, and generating code from them. I don't mean so much Agda, Idris, etc., which try to approach theorem proving from a programmer's point of view. But theorem proving systems that embrace the full spectrum of abstract and correct thought. Zig and Rust try to get there without paying the heavy price that theorem proving incurs (Rust pays that price more than Zig already, though). But to truly progress we need to pay that price and make it lower and lower with time.
- lenkite 6y agoWould love a real programming language where one can write subroutines and structures that are directly checked for logical correctness and performance. Having to rewrite something in an alternative language to prove correctness is a major pain.
- giraj 6y agoCould you elaborate or illustrate what you mean by "theorem proving systems that embrace the full spectrum of abstract and correct thought"? As a mathematician (and programmer on the side) who regularly works in Coq, my impression is that Rust does represent "the future of programming" (or rather, my ideal of it). Type systems are the only mechanism (that I know of) for formally ensuring properties of programs, and proof assistants and languages like Rust lie on two extremes of the spectrum. The former puts the type system in focus; indeed all my work in Coq is about convincing the compiler that certain functions (terms) type-check. The latter puts types in the background, trying to prove as much as possible with minimal friction. What's the alternative?
- auggierose 6y agoVery interesting, I don't know many mathematicians working in Coq. What is your field?
- giraj 6y ago
- xiaodai 6y agothis reads like a really well researched post
- junon 6y agoZig has very little focus on safety and has had several regressions related to security and correctness, most of which Andrew has said he doesn't care about as much. This concerns me greatly. The discord community operates like a cult (sound familiar, Rust community?) and any criticisms or anything not exuberantly positive results in a flame war. I saw all of that happen several times so far with the community and it's ultimately what drove me away from the project altogether.
- gameswithgo 6y agoEvery language has overzealous fans, when a language is new this is likely a largely percentage of the community. If you use that as a reason to avoid a language you will likely just end up avoiding new languages.
- junon 6y agoSee my other comment. It's more the fact Andrew stepped into the conversation himself and just said "be nice" instead of addressing security concerns, just fueling the flame war even more. A response to a security concern should never be "fuck off".
- deleted 6y ago[deleted]
- geodel 6y ago> A response to a security concern should never be "fuck off". It can be when it is out of context and/or out of proportion. But it could be more likely that temperamentally you are disposed towards different programing language where security concerns must override any and every issue at hand.
- drcode 6y agoAny new language project in 2020, for good or ill, is going to be highly opinionated and self-select for people who share similar views on language design. When someone comes along on their discord and says "you need to have this security and correctness issue that I care about deeply fixed yesterday" you really can't be surprised if not everyone shares your urgency- There are probably other languages/communities that DO care deeply about exactly those types of issues, that's the beauty of the long tail of the internet.
- dnautics 6y agoI believe if you write tests hooking up your tests with the test allocator will effectively prevent all blatant UAF and memory leak events. More subtle ones that happen due to spooky action at a distance and wierd incomposability might be out of reach. (Not at op, who does write tests:) You are writing tests, right? ;)
- jamii 6y agoTesting is a good way to ensure that your program won't have UAF under most normal circumstances. But when it comes to security it's adversarial - your program will get pushed into parts of the state space that were never seen during testing. Things like browsers and operating systems are all heavily tested and fuzzed using tools like asan. They still have security issues from UAF.
- dnautics 6y agoif you're worried about errors coming in through testing and fuzzing, you could be just as in trouble in a language as rust due to, say an unsafe block not composing well with another unsafe block two dependencies over. The challenge then is to figure out how to debug, I would worry that obsession with "zero-cost abstractions" making your code difficult to reason about and obscure the bug more than a system that has a more barebones relationship with the computational processes. However, only time will tell which is the better strategy.
- boterock 6y agoI lost interest when I tried it and it wouldn't compile code with tabs. (Don't know if it is still the case though)
- steveklabnik 6y agoYes, but eventually, no https://github.com/ziglang/zig/wiki/FAQ#why-does-zig-force-me-to-use-spaces-instead-of-tabs https://github.com/ziglang/zig/wiki/FAQ#why-does-zig-force-m...
- andjd 6y ago> As long as all unsafe code obeys the aliasing and lifetime rules, rust protects completely against UAF. Zig has little protection. This is the most relevant part to me. As someone who will probably never write a line of either myself, the way I will work with languages like this is thorough libraries or extensions of higher-level languages like python or ruby. To that end, safety is the most important factor to me, with performance very much second. While rust has unsafe operations, these are relatively easy to audit if the code is open source. Ok, so to the programmer, Zig may be more ergonomic than C or Rust. But until Zig can offer the safety assurances of Rust, I'm still rooting for Rust to take over the world as the dominant and de-facto low-level language.
- haberman 6y agoVery interesting set of observations. As a C and C++ programmer, I am feeling more and more excited about Zig. One thing that feels missing from Zig though is encapsulation. I don't believe that you can declare struct fields private, such that they can only be accessed by methods. This seems really important to me, not only as a technical means of enforcing invariants, or hiding internal-only implementation details that may change, but even just as a communication of intent. There is a big difference between "you are invited to read/write this directly" and "please don't read/write this." C doesn't have this, true, but at least in C you can put internal-only structs in a .c file instead of .h, making the members effectively invisible to clients. Does Zig have anything comparable?
- murkt 6y agoIf you want to communicate intent, put an underscore in the field name, like it's being done in Python. `field` is "you are invited to read/write this directly", `_field` is "please don't read/write this."
- penguin_booze 6y agoAs Raymond Hettinger once remarked, Python is a language for consenting adults.
- dnautics 6y agoI bet it wouldn't be hard to write something in zig that enforces this. I think the compiler is going to start getting pluggable and when a package manager comes out I'm sure something like an opt-in check against this will become a thing.
- mumblemumble 6y agoEven in a lot of languages that do have "private", it's still pretty consent-based. Java and C#, for example, don't prevent you from mucking with private members, they just make it inconvenient to do so. I used to be suspicious of the Python approach, because it doesn't even pretend it's enforced by anything but the honor system. But I've discovered that, in practice, my Python-using colleagues are no less likely to respect `_field` than my Java-using colleagues are to respect `private field`.
- skohan 6y ago> Zig is dramatically simpler than rust... Most of this difference is not related to lifetimes. I've been thinking about this recently: the borrow checker gets a lot of attention, but I think the majority of the learning curve of rust is actually due to "unforced errors" in the language UX which have nothing to do with the language's core USP's. For instance, the module system just seems needlessly complex. Like you need a mod.rs file in each subdirectory, and the main.rs/lib.rs serves as the module file for the src directory, it took me like a day to figure out what exactly the rules are, and I can't understand why there can't be sensible defaults to define modules based on the file system structure alone.
- steveklabnik 6y agomod.rs isn't needed anymore; back in 2018 we fixed that: https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html#no-more-modrs https://doc.rust-lang.org/edition-guide/rust-2018/module-sys... > why there can't be sensible defaults to define modules based on the file system structure alone. There could be, and in fact, I personally advocated for them. But there was significant community pushback; it turns out many people like to "comment out" entire modules when doing big refactorings. There are also some interesting edge cases, for example, you can use a #[path] attribute on a module to change what the path to the file is; without a mod statement, it's not clear where that would go.
- skohan 6y ago> mod.rs isn't needed anymore; So I'm not sure I'm a fan of this either. So now for a module with sub-modules, part of it is defined in the top-level directory, and part of it is defined in a sub-directory. Now if I want to move a module from one directory to another, I have to move two items in the file system. > it turns out many people like to "comment out" entire modules when doing big refactorings. It seems to me it would be much better to have an "exclude.rs" file or something to opt out of a sensible default rather than forcing extra work in the common case just to support the common case. Or else you could still allow a mod.rs if you want to be explicit about your module contents, and just assume it includes everything in the directory if it's missing > There are also some interesting edge cases, for example, you can use a #[path] attribute on a module to change what the path to the file is; without a mod statement, it's not clear where that would go. Again, I don't think a design should be optimized to support interesting edge cases. It should make the common case as simple as possible. If edge cases need to be supported, I'm sure a solution can be found
- 978e4721a 6y agoRust restricts compile time code to what t can prove. Exactly! That's a feature. Rust is a step forward to a future where code is based on sound theory (type Theory) not on some ad-hoc "seems to be working" basis.
- zem 6y agomy ears pricked up at the mention of gtk apps for the pinephone; i was planning on exploring rust and D to do precisely that. will definitely add zig to the mix now.
- jamii 6y agoHere's how I set up the cross-compile in zig - https://scattered-thoughts.net/writing/pinephone-first-steps/ https://scattered-thoughts.net/writing/pinephone-first-steps... That only covers gl/sdl but I think gtk worked with that method too.
- zem 6y agothanks! when i had my n900 i used vala for the same use case (small, quick apps that were just for me to use) but the language is getting a bit long in the tooth :)
- galois198 6y agoZig is a very well thought out, pragmatic language. I really want to use it more, but I cannot get past the syntax. For example, method chaining: std.fs.cwd().openFile("does_not_exist/foo.txt", .{}) The aesthetic of a language is really important to me, and method chaining that allows for lines like this somehow doesn't feel right.
- dinglejungle 6y agoAre you saying that the given example should be disallowed?
- fluffything 6y agoWhat do Zig "generics"/"comptime" errors look like ? > One of the key differences between zig and rust is that when writing a generic function, rust will prove that the function is type-safe for every possible value of the generic parameters. Zig will prove that the function is type-safe only for each parameter that you actually call the function with. On the one hand, this allows zig to make use of arbitrary compile-time logic [...] This is fundamentally identical to how C++ templates, constexpr and concepts work. Its a really flexible system (you can implement how you want to type check things using constexpr), but has three cons that Rust system does not have: - can't typecheck library APIs, so library authors aren't sure if their "constraints" are correct. Testing this requires writing lots and lots of compile-time tests. - errors deep inside a library implementation when user code passes incorrect arguments to generic APIs. - rust traits can be used for static dispatch, or boxed and used for dynamic dispatch, C++ at least can't really do this well. It would be cool if someone could explain how Zig fixes or improves upon these problems that this system has in C++. C++ tried to fix this with concepts, but failed. This was a nice read that has motivated me to learn Zig. I want to know how Zig improves on these C++ issues.