7 ms·
A Comparison of Rust and Zig
- pornel 5y agoRegarding lifetimes: they're severely overused by people new to Rust. Types like struct <'a,'b,'c,'d> are not for storing data, but are temporary views into data stored elsewhere. If such struct didn't have to be forced to be temporary, or wasn't a view into pre-existing data that can't be moved, then it wouldn't need the lifetime annotations. Such lifetime salad can happen in very specific scenarios, but they're rarely needed in most programs. It's very unfortunate that the Rust's feature for temporary, scope-limited loans, AKA references, looks so similar to general-purpose features that other languages have for referring to objects or storing data by reference. It's not the same thing! Rust has a few other constructs for referring to objects, like Arc which is a shared reference, but it's not temporary, or Box which is a pointer for storing data by reference, but doesn't have a lifetime attached. When you start taking ownership into account when designing your data structures, it often turns out that Rust references are not the right tool to use. In 99% of structs references are a mistake (but they exist, because there is that 1% of legit cases).
- sph 5y ago> Types like struct <'a,'b,'c,'d> are not for storing data, but are temporary views into data stored elsewhere. Thanks. As someone that still has a hard time with appreciating the complexity of Rust and its lifetimes, this explanation of yours is illuminating and should be prominently featured in the documentation. It makes a lot of sense and answers a lot of doubts I had.
- cweagans 5y agoAs someone who has tried to like Rust repeatedly, I found that a collection of "gotchas" like this extremely offputting. Is it better for people to unlearn a common thing to do it the "rust way"? Or for Rust to make itself a bit more ergonomic to prevent a common mistake? I understand the importance of thoroughly learning one's tools, but this seems a lot like the ol' "you're holding it wrong" argument. I will also acknowledge the possibility (and likelihood) that I don't understand Rust well enough to dissect the nuance here, but I'll also point out that I really didn't have to dig in that far to gain a working understanding of Go.
- axiosgunnar 5y ago> I personally really dislike the degree to which rust and zig force the programmer to worry about error handling. The way I see it, things should not error, if they do, stop everything and print a stack trace, please don't bother me with it. Then just .unwrap() everything?
- deleted 5y ago[deleted]
- unrealhoang 5y agoOr use `anyhow` and `?` everything.
- agubelu 5y agoPersonally, I really like error handling in Rust. It makes me think about handling deviations from the happy path much more than exceptions ever did. As you said, you can choose to .unwrap() everything and that's fine if it's what you want, but it forces you to make a conscious decision. I like that explicitness.
- vlmutolo 5y ago> The aspect of rust about which I am most deeply dubious is the heavy use of macros: they are a herald of rust's pedantry ("If you don't use these macros you are going to have to write a shit-ton of boilerplate code.") The most-used, most-loved macros in Rust have nothing to do with Rust's "pedantry", but rather the programmatic derivation of business logic that would have been annoying to write. Serde's Serialize and Deserialize macros are amazing for creating complex and correct logic for shuffling data into and out of various serialization formats. Structopt's (and now clap's) macros for command line argument parsing make writing CLIs a piece of cake. Arguably this is just another form of deserialization. Still great, and unrelated to the fact that Rust is verbose and explicit. I guess you could argue that something like the PartialEq derivation macro is only necessary because Rust forces you to explicitly declare how user-defined types compare to each other (<, >, =, etc.) But I think this is super reasonable, and if you accept that, then the macro just provides a shortcut to opt-into the code that most people might want, which is that structs will compare using the comparison functions of their inner fields.
- barchar 5y agoMacros get better as more people use them anyway, you build up a community knowledge of what the hell they generate. It turns out that unhygenic macros are really rather useful, if a bit dangerous.
- zarzavat 5y agoGC, manual malloc/free, or lifetimes and macros: choose one. Rust would be miserable to use without macros. There's so much ceremony and friction to ensure memory safety that you need a powerful macro system to cut through it sometimes. If you don't like the macros then you can always use a managed GC language that gives the same safety guarantees at a performance cost.
- ameliaquining 5y agoWait, most uses of macros in Rust don't have much to do with memory safety. RAII and borrow-checking don't rely on macros at all. Most features of, e.g., serde would work the same way in a garbage-collected language. The primary exception that comes to mind is pin_utils::pin_mut, which is a macro primarily because of the somewhat circuitous route by which async came to Rust. If the language had been designed to support async from the beginning, I suspect that it probably would have been built-in syntax (assuming it didn't avoid the need for such an API entirely by using a different design).
- verdagon 5y agoI do a lot in the PL space, and Zig has me pretty excited. I think Zig, paired with memory tagging for memory safety, could be a killer combination one day.
- barchar 5y agohave you looked at ParaSail. It's designed by the guy who led the Ada 9x ('95) design team and it's unique in being pointer free (not just like in ada or Fortran where pointers are discouraged, but the language literally does not have them). Instead of pointers it has grow-able types based on memory tagging. It's specified such that each sub-expression can be safely executed in parallel.
- pjmlp 5y agoParaSail development has stagnated after he joined Ada Core, have any other progresses happened since then?
- pjmlp 5y agoGiven that memory tagging for memory safety has been designed for C and C++, already exists for SPARC ADI, and is getting adoption across ARM based devices specially iOS and Android ones, what is the improvement Zig brings to the table on that regard?
- barchar 5y agoI'm definitely a massive fan of some of zig's ideas I like how flexible structs are, and the inclusion of structural subtyping for them. I do wish C's unnamed fields made an appearance, however. The way type constructors work is also really great. It's just "yeah it's literally a constructor that returns a type, like the same syntax and everything" I'm a little concerned about how long zig is willing to defer semantic analysis and possible difficulties with symbol binding in generic code (the binding rules end up being closer to C macros than C++ templates I think). But, the language doesn't have overloading, which makes the symbol binding problems much, much, much less severe. It has a lot of the same qualities that make me like C. It can represent the data structures I want to write and the code I want the compiler to generate, it makes using it's escape hatches ergonomic, and it's obvious what's going on, especially to unfamiliar folks looking at zig code in a debugger. Also embedding a full C compiler into the zig compiler is absolutely the correct way to do FFI. C++ level compatibility with C without all the baggage (it's kinda amazing how annoying C's conversion rules are once you add overloading and templates tbh). It really is _staggering_ how long the compiler will defer actually doing sema and generating real code though, so we'll see.
- 3a2d29 5y agoMan, I am a young junior developer so I am not aware of all the history of programming, but this past decade really seems like the renaissance of low level programming (at least in terms of new languages) On another note, as a fan of both C and Rust, I am excited to see new languages that might improve upon ones that I already love. My big question for Zig is, is this going to be like the D programming language? From what I was told D was better than C++, but wasn't enough of an improvement to fully take hold. I do like the idea of an improved C though. For as good as Rust is, it certainly can take a while to code up a project, I would prefer C to fire things up quickly and having some modern improvements in a language like C definitely grabs my attention.
- renox 5y agoD is chock-full of very good ideas but original-D has a GC so it's not really a competition for C++. DasBetterC has no GC but it's quite new so it must compete for mindshare with Rust, Zig, V, Odin..
- pjmlp 5y agoIt was all the way like this until everyone decided it was all about Java and scripting languages. Go into online archives read magazines like Byte or DDJ, they are full of ads for tons of languages.
- pjmlp 5y agoTraits support inheritance, trait Widget { fn draw(); } trait Menu : Widget { fn on_select(); } Most package managers are older than cargo, so it is a bit hard for it to have had an influence on them. Unless zig makes it a compiler error to forget to use defer, forgetting to delete memory is still a possible scenario, and even then it does very little to prevent an use-after-free.
- ayende 5y agoThe default Zig allocator will actually make it very hard to have unnoticed use-after-free. It doesn't re-use the virtual address space, it will discard the page entirely and get a new one, that make it far more likely that a use-after-free will get a segmentation fault.
- the_why_of_y 5y agoWouldn't that require allocating a full page for a 1-byte object? That would be rather wasteful, both in space and in munmap(2) overhead.
- pjmlp 5y agoBasically the same as debug allocators for C and C++, what is the improvement there?
- sealeck 5y agoSupertraits aren't inheritance!
- pjmlp 5y agoYes, they are, brush up your knowledge on OOP, plenty of SIGPLAN papers available.
- IshKebab 5y ago> The problem [with self-referential structs] seems to be that there is no way to promise rust that you will deallocate the entire struct properly. That's not the problem. The problem is that Rust doesn't have move constructors. A move is just a memcpy. If a struct contains pointers to itself (in this case the references are just pointers) then memcpying it will invalidate the pointers. I agree it's one of the biggest annoyances with Rust. For example a lot of parsing libraries return a parse result that contains pointers to the `&[u8]` that you gave it. If you want to return the parse result up the call stack past where the input comes from you end up with a self-referential struct and it's pretty much impossible. There are complicated solutions but frankly they're all so complicated I've always given up and just copied the data or rearchitected the program.
- newaccount2021 5y ago