Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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. 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.



My 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.


Rust 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.


That'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.


Rust 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.


I 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/

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.


You're comparing 35-year-old C++ with 10-year-old Rust (and that doesn't even capture the story, because after two years C++ had something like 5x the market penetration Rust has after ten). But even now Rust, I think, together with C++, Ada, and Scala, easily ranks among the top five or so most complex programming languages in software history.

It's definitely a matter of personal taste, but to me, Rust seems a monumental, awe-inspiring shrine erected to worship at the altar of accidental complexity. I admire the technical achievement -- I never imagined accidental complexity could be given such spectacular prominence -- but having spent my share of time with both C++ and Ada, I'd like to look elsewhere. I don't know if Zig will do the job, but the vision it has for low-level development is so refreshing, radical and different from everything else I've seen in my >20 years of professional software development that I'd like to give it a chance before settling for an improved C++. Anyway, it's a matter of personal aesthetic preference.


Four of the five pieces of accidental complexity I listed were present in C++98, so I'm not sure what the relative ages have to do with this. (You've also not mentioned any accidental complexity in Rust...) Instead, I attribute it to hindsight and differences in priorities- C++ today is still introducing new features with the same level of unforced complexity (e.g. compare C++ lambda capture clauses and coroutines, to Rust closures and async fn), while additions to Rust instead tend to "fill in" inconsistencies to remove edge cases and exceptions.

I'm also not trying to convince you to drop Zig and settle for Rust! You can use either or both or neither, I don't mind! Rather, my point is that Rust's direction relative to C++ is the same one you praise Zig for- it provides the same control with drastically more economical application of fewer language features. Just because Zig goes further (again, at great cost to things like tooling, error checking, and messages) doesn't mean Rust didn't make a lot of progress.


Sorry, I don't see it that way (and I don't entirely agree with your characterisation of what new Rust changes do, certainly not all of them). I think that both Rust and C++ are fundamentally built around a design concept that I find distasteful and wrong-headed for low-level programming -- https://news.ycombinator.com/item?id=24840818; I guess you can call it too much implicitness aimed to make the language appear something it isn't when printed on the page. Rust does it somewhat more elegantly than C++ and perhaps has other justifications for it (like sound guarantees) -- which is why I prefer it to C++, although not enough to justify a large investment until it has a significant market share -- but it espouses the same foundational design aesthetics. I think that whether people like or dislike Rust mostly has to do with whether they find that aesthetic appealing. I'm sure many people do and many people don't.

BTW, I haven't "adopted" Zig that I would need to drop it for anything (it's not even 1.0 yet). I'm still with C++ for the time being. But I'm deeply impressed by Zig's revolutionary design and complete rethinking of low-level programming that I'm keeping a watchful and hopeful eye on it.


That's much closer to a substantial and fair similarity between Rust and C++! Indeed, if I understand you correctly, it's a major cause of both languages' slow compile times, and a source of a lot of difficulties for humans learning and writing both languages.

But this makes me suspect we may be using very different definitions of "accidental complexity" here: Your usage seems to apply to programs, which wind up over-specifying low-level details in both languages. My usage of the term applies instead to the languages themselves, and the level of extra pain they inflict on programmers who have already accepted the C++/Rust/etc aesthetic.


By accidental complexity I mean aspects that go beyond what you would write in pseudocode when describing an algorithm, and in a language it means the number and "depth" of features dedicated to those aspects relative to the language's total.


> because after two years C++ had something like 5x the market penetration Rust has after ten

C++ is highly backwards compatible to C to the degree that it's almost (but not quite) a superset. Of course it's quite easy to start creating cpp files. Also C++'s benefits were quickly realizable while for Rust's safety benefits to play out, you need to have replaced significant portions of highly risky components (e.g. those that parse user data, have a history of bugs, etc). Adding Rust to an existing C++ codebase is much harder than adding C++ to an existing C codebase.

Also do you have a link for your claim? I'm interested in reading on the early rise of C++.


> There is even undefined behavior if you mess up namespacing! (Undiagnosed ODR violations are every experienced C++ programmer's nightmare.)

so, uh, how does Rust define behaviour if you export a same-name symbol from two rust dlls and load it from some executable ?


It carefully designs its name mangling such that this doesn't happen in the first place, even in the presence of multiple slightly-different builds of a library.

The only way to get matching names is to ask for them explicitly, via FFI.


> It carefully designs its name mangling such that this doesn't happen in the first place, even in the presence of multiple slightly-different builds of a library.

but, after checking apparently this adds a hash of the function to the name mangling - how does that work when you want to call it from another language ? e.g. for instance you can call C++ code directly through some dialects of Lisp, Perl , ADA, or D (AFAIR) as they all have libs or mechanisms that kinda understand C++ name mangling - how are you going to do the same with, from what I'm seeing, "name_of_the_file::name_of_the_function::some_hash" ?

> The only way to get matching names is to ask for them explicitly, via FFI.

and what happens if you have two libraries which expose the same extern-C function name ?


If you want to call Rust code from another language, you use its FFI tools to export an un-mangled API. This is typical for C++ as well- trying to interop with mangled C++ names requires a lot of coordination across the toolchains and so even the examples you cite don't work without a lot of pain.

If you do wind up exporting the same name twice, you just get a linker error, because Rust doesn't play the same games C++ does with linkage. (This is also true of C++ FFI- the problematic ODR-violation stuff tends to involve more complex language features than `extern "C"`.)


> If you want to call Rust code from another language, you use its FFI tools to export an un-mangled API.

this does not answer the question of whether the behaviour is defined if multiple libraries export the same name (which is the original question). See my other comment, what happens if from rust code you dlopen libbar.so ?


The behavior in that case is defined by the implementation of dlopen. This is entirely outside of Rust's control, but fortunately it's also perfectly well-defined by the platform. Again, does not intersect with the ODR violations I mentioned originally.


> but fortunately it's also perfectly well-defined by the platform.

that's the same for every language and thus not very relevant. if you have single, static binaries / libraries of course everything is simple, and you'll get linker errors in C++ just like you would in Rust. What is not simple is when you start loading twelve dozen libs at load-time or run-time and it does not seem that Rust defines behaviour any more than C++ in that case.


Right, I've been telling you it's not relevant for the past three comments now.

You seem to be under the impression that dlopen somehow interacts with language undefined behavior; it does not in either Rust or C++.


> Right, I've been telling you it's not relevant for the past three comments now.

but it is ! the only reason why ODR is UB in C++ is because the C++ language authors can't force the system linkers (again, whether at link time, load time or runtime, I'm not only referring to dlopen) to perform LTO which trivially makes ODR violations a diagnosticable error.

But as far as I know, neither can the Rust language authors do so - so either the behaviour in Rust is as defined as in C++, or Rust does not support creating standard platform object files that are linked by ld, gold, or whatever (such as D, ADA, Fortran etc all support) which would make a fair amount of use cases impossible - it's pretty common in some HPC circles to link C++ and Fortran directly in the same executable for instance. And then, of course it's easier to define behaviour when you use a reduced set of constraints, but it definitely does not makes something worth bragging about.


Going back to my original answer, the difference lies in what the two languages ask of the linker under normal circumstances.

Normal, non-FFI-using C++ can hit ODR violations in response to things like typos or subtle mis-uses of `inline` and templates.

Normal, non-FFI-using Rust is designed such that these situations never come up.

My original comment was never talking about FFI in the first place, where yes, both languages are much more at the mercy of what the platform provides. However, in that case the spooky UB ODR violations I was referring to are also not relevant, because you just get normal, fully-defined platform behavior- the expectations of the compiler (and thus the chances for them to be violated, resulting in UB), are different.


carefully designed LOL what kind of joke is this

    $ echo "pub fn my_function() -> i32 { 0 }" > bar.rs ; rustc --crate-type=dylib bar.rs && nm -A libbar.so| grep my_fun
    _ZN3bar11my_function17hce21faeb92ac13c6E

    $ echo "pub fn my_function() -> i32 { 1 }" > bar.rs ; rustc --crate-type=dylib bar.rs && nm -A libbar.so| grep my_fun
    _ZN3bar11my_function17hce21faeb92ac13c6E


You missed the c++filt at the end:

$ echo "pub fn my_function() -> i32 { 1 }" > bar.rs ; rustc --crate-type=dylib bar.rs && nm -A libbar.so| grep my_fun | c++filt

libbar.so:0000000000047230 T bar::my_function::h4ed6ea856a52cd6b

So adding a single hash to the end of the symbol is a joke?


I don't think that dlsym would accept "bar::my_function::h4ed6ea856a52cd6b" as a symbol, just like it would not accept "foo(std::vector<int, std::allocator<int> >&)" and wants "_Z3fooRSt6vectorIiSaIiEE" instead, no ?

To be clear, my comment was about the fact that two different functions produce exactly the same symbol name, c++filt or not, which is not what I was told above in " It carefully designs its name mangling such that this doesn't happen in the first place, even in the presence of multiple slightly-different builds of a library."

I have no particular comments on the idea of using hash though I believe that something that changes 95% of chance error in 0.5% of error (I'd assume, as it took me 10 seconds to find a collision) is very bad - you want errors consistently when you fuck up, not once every hash collision as it sounds like a really really big pain to debug when it happens.


I see, I misunderstood your comment as about the mangling convention, not about the collision.


While 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.


I have the feeling that what will push Zig ahead is the uncanny sense of technical aesthetics shown by the creator. Unsexy details like language simplicity, what-you-read-is-what-runs, orders of magnitude faster compile times, effortless cross compilation and C interoperability.

I have never seen anyone focusing so ruthlessly, so early, on nitty-gritty details of how to go about engineering a compiler and language that is and will let you be as close to optimal as possible in terms of compile-time and run-time performance. "Perfect software" as Andrew talks about.

In short, engineering choices taken early will let Zig be something that Rust maybe could approach too (in theory), but in practice never will. Of course Rust is something that Zig will never be, too.


This is excellent point. Zig authors like Go authors prioritize software engineering and hence upfront work on tooling, fast compilers, cross compilation etc. Whereas a lot of upcoming languages in last decade are prioritizing PL design part, so tooling will be delegated to external projects.

I have seen many say just combine PL design innovation and tooling part and it will be perfect but I think this will not happen because it becomes very difficult and sensibilities of these approaches do not match.


I also think that engineering concerns can dictate or at least strongly influence language design. It can still lead to innovation though, like in the way Zig handles async/await, and how it will try to handle recursion.

It is just innovation mostly guided by practical engineering concerns. In this case: How to avoid requiring expensive heap allocation for async tasks, and how to be sure you don't crash from a stack overflow due to unexpected input for example.

I suspect that the excellent Zig comptime support is made possible by intentionally going for a pretty unambitious type system. Otherwise how could you soundly let arbitrary code generate a type? A fancy type system that wants to reason about that will fail, or at least cause the whole language to revolve around making that work.

I think you are right that it would be very hard for a language to innovate both here, and in the traditional PL-teoretical way.


Rust 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++.


The more I use Rust, the more it doesn't remind me of C++, but instead the C structure of larger projects. C (and really any language probably) kinda feels like a different language with each few orders of magnitude in code size. Rust feels a lot like forcing the structure of 10KLOC to 1MLOC C projects to me with sane defaults and more powerful versions of the same abstractions.

We're not using string based macros, but hygenic macros.

We're not using goto error, but instead RAII.

We're not guessing if a function returns 0 or 1 on success and if the big struct we passed in as a pointer is valid on either of those, we're using an ADT that makes it clear.

Slices mean everyone aren't reimplementing 1000 morphs of the same buffer struct.

And the deeper separation between code and data that the fat pointers give you and how the more you go down OO principles, the less idiomatic it feels, really feels more like a giant C codebase than a C++ one to me.


I agree (I think), Rust is more like C++ and less like C in that it provides mechanisms to enforce certain "custom rules" in bigger code bases and teams via language constructs. Those same mechanisms which help organizing big code bases written by big teams often increase friction in smaller codebases and teams.

In C one has to be very disciplined about this type of stuff and needs to put much more thought into module API design (for instance to enforce memory management rules), because the language itself is extremely "freestyle".


> Rust and Zig target different audiences though.

You would think that, but for example I think that, for example, Oxide should definitely have picked zig, if it were more ready. Obviously it's not, and oxide wants to ship now.


> 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.


> ... for any of them to be using C over C++ these days.

That's the old (and frankly: tiresome) mindset that C++ is a successor and improvement of C. After using "modern C" (as in C99 or later) for a while it becomes quite obvious that this isn't the case anymore, instead C++ was a fork of C and developed into a very different direction (including developing the original C subset into a non-standard C dialect). Especially with more recent C++ standards, C and C++ have become different languages with very different goals.


> That's the old (and frankly: tiresome) mindset

It really isn't. It's based on comparing the languages as they exist today. C++ is vastly more productive.


C++ is also faster thanks to template specialisation and the quick way you can make the right data structure.


Zig has high interoperability with C and plans to have a C backend as well.

https://github.com/ziglang/zig/issues/3772


> 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.)


Agreed. 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.


>has nothing that other languages couldn't copy or assimilate

What about extremely fast compilation? In theory a complex language could have a really fast compiler, but I don't think there are any historical examples of languages with a very slow compiler getting compilation down to around one second as the article describes.


D language is currently experimenting the ownership model made popular by Rust/Cyclone and similar to Zig it has excellent CTFE support:

https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in...


For 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.


For 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.


It 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.


Never 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.


For 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.


Maybe it's because Rust is backed by Mozilla, which is a bigger player than Zig's creator?


Rust 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.


That if anything would have made me more suspicious. Mozilla had and still has an awful track record of keeping projects alive.


I 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.


In my experience software developers getting excited about their tools rarely lead to good outcomes for the projects they're working on.


Same. Make the tools boring so the excitement can go into the product.


> 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.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: