To wit, C++ is a brutal, terribly designed mess that's absolute torture to work with.
Rust is extremely well-designed and a pleasure to work with.
Very important difference!
The current trend of combining the two concerns by having language-specific build systems (like Cargo) was a mistake, IMO.
Language rules define dependency rules, that the build system is not aware of. For example transitive #include chains, requiring stuff like gcc -MMD to generate makefiles. This also improves IDE experience with suggestions on where to find imports instead of elementary error messages like "header file not found".
Also, the need for header files and disgusting hacks like unity builds just adds unnecessary friction.
even if you could incorporate external libraries with zero impedance mismatch, you still have to _use_ the said libraries.
while there might be _marginal_ element of truth to that statement, however ease of stitching external libraries into your application has no bearing on either the programming language or the act of programming the application itself.
simple, ease of integrating external libraries into a project has _nothing_ to do with the programming language.
they are at best two orthogonal topics.
This is not mentioning all the upsides of having a easy build system from easy learning and community standpoints.
In fact, Go and Rust are both evolving fast compared to C (and maybe even to C++) and interestingly enough, most of the time the evolution comes in similar domains (error handling, modules, generics).
The big difference is the release schedule: Rust release small changes often while Go release big changes rarely. The 6-weeks schedule has advantages (each version is quite small so it's easier to test and bugs are found earlier) but it adds a feeling of churn which can be harmful.
The blog post we're all commenting on started because someone took umbrage to an idea someone else expressed in another blog post. Not because of any actual language changes, not even a formally proposed change, just an idea in a blog post. All the actual changes mentioned ("try!() -> ?, the addition of impl trait, and the dyn keyword") happened years ago. And not all at once. It took years from adding `?` to adding `dyn`.
The rate of adding features is quite slow, as demonstrated elsewhere in this thread. Important features spend a long time baking, and a long time in testing before they are piecewise stabilized.
> When a new version of Rust is released, the core Rust devs write a blog post gleefully explaining all the new "features" the current version has.
Of course when someone talks about a CHANGELOG, they'll talk (rather happily) about the things that have changed/improved.
How so? Aren't await and ? both new special cases?
Take the Rust 1.42 release, for example. Way back in Rust 1.31, we removed the need for you to write `extern crate`. But when you were writing a procedural macro, you still had to write "extern crate proc_macro;". With Rust 1.42, you no longer have to, and it just works, like everything else. This removed a special case.
Rust has been doing a lot of the "this was special cased to ship, now we are making it more general like we always planned" lately.
As for your second question, async/await is absolutely the biggest thing added since 1.0 (and is an outlier in new features in that regard); the ? operator is just an improved version of the try! macro that shipped with 1.0.
Also note that 'evolving-by-reduction-rather-than-addition' philosophy is only part of Wirth's Oberon linage.
Oberon-2 (which Go got its method syntax from), Active Oberon, Component Pascal and Zonnon, are all Oberon descendents from ETHZ as well, which go into the direction of mainstream languages, including support for generics or more low level programming capabilities.
"The CSP paradigm" comes from CSP: https://en.wikipedia.org/wiki/Communicating_sequential_proce... which predates the vast majority of Rob Pike's work (eg: before he went to Bell Labs).
- Lower spec devices have a hard cap on overall code complexity imposed both by available ROM or flash constraints (big projects literally won't fit on the chip), and by time constraints (if your chip is running at <= XX MHz when it's active, you don't have time to run very many functions between events or interrupts). Most projects for these devices won't grow to the point where you really need the code organization benefits that C++ provides.
- It's a lot less effort to port or implement a C toolchain for your chip than it is to port or implement a toolchain for a more complex language like C++, Rust, or even Ada. It's not just the compiler - you also have to have a working standard library (even if some functions are just stubs), an interactive debugger, and integrations with IDEs (if you already provide that for C). All that software engineering is expensive, and you have a much smaller market of developers to amortize that cost over.
These constraints aren't as binding for high-production, higher-spec devices like popular families of ARM Cortex-M chips, so usage of C++ seems to be relatively more popular for those devices. Even then, embedded work normally requires more of a "C with classes" or "C plus the std::algorithm library" approach, which is different from C++ projects you'd see that target servers.
And in a way it is :) given who worked on it, and where it was created.
Despite my opinion regarding its design, Go was definitely a C replacement for F-Secure TamaGo, Google's gVisor, Android GPGPU and CoreBoot projects.
I haven't used Go beyond tutorials and some very basic programs, but I am pretty comfortable with Java. What about Go makes you feel it's relatable to Java? From what I've read the lack of generics is a hot topic in the Go community, but they're pretty crucial to most programs written in Java. Is this still true?
You may remember that in Java 1.4, `get`ting from collections (e.g. ArrayList) always returned Object, which you were expected to cast to their runtime type (or a class that their runtime type inherits from). I was young at the time, correct me if I'm wrong. Contrast this with Go's solution, which seems to be user-inaccessible compiler magic. I much prefer Go's solution to Java's, but I also like generics.
In reality Go can fully take over Java's problem space.
Or, when Project Loom releases, Java will be able to fully take over Go's problem space.
Just today someone posted in /r/rust about how they took a two year old project, compiled it with the latest compiler, no issues. Other than it magically took less time to compile and the end result ran faster.
I have it straight from the horse's (Bjarne's) mouth that there will be no more editions of "The C++ Programming Language" because the language is too big and writing a book too time consuming to actually cover the language as it currently exists and what it is going to become. It's own creator admits that the language is too big to document in book form. How does one ever expect a beginner to get to grips with it in that case!?!
Obviously Rust isn't there as it doesn't have 30 years of evolution and development like C++ does. But the rate at which it is growing and changing means it won't actually take 30 years to become the new C++. And that would be a real tragedy. What's the use of all these awesome safety features if they're in a language almost no one will be able to fully comprehend or understand?
> But the rate at which it is growing and changing means it won't actually take 30 years to become the new C++.
I very, truly, seriously doubt this is true. First of all, as I said elsewhere in the thread, Rust has been changing very slowly lately. But beyond that, a significant reason for C++'s complexity is that it's sort of two, possibly three different languages: Modern C++ is very different than C with Classes. It is a miracle that they retrofitted a new language on top of an old one, but there's no indication that Rust will ever do that. The quantity of change is one thing, but the qualitative aspects matter here too, and I feel like you're only considering the quantitative aspect (which, I also disagree with, to be clear.)
Auto is the only one I can think of.
(I am just curious, I have no idea how common your parent's comment is in real life.)
Code that doesn't rely on soundness bugs written in 1.0 should generally compile on the latest stable release without issue. The vast majority Rust users in our annual survey report that their code never breaks, and of those that have, the majority have said that it is trivial or easy to fix.
We put a tremendous amount of work (and spend a lot of money!) into ensuring this. If upgrading your Rust compiler is a significant issue for you our anyone else, please report these things to us.
Upgrading my compiler isn't an issue. I haven't opted to transitioning to Rust at all in the first place until things calm down. I've yet to feel that my career has once ever been impacted by not learning it.
I'm in hardware land, not JavaScript frontends. Innovation there is more about the product features, sensor capabilities, power consumption. Generally solving customer issues.
Selling adoption of a new language to the superiors or new job interviewers to use in production isn't an issue about compiler upgrades or the merits of using cargo fix. They are more concerned about how many people are available to be hired that has extensive expertise in the language, making hiring hard, and support is still in flux. And that is what I'm reporting, tooling is more than about the compiler, the world around it and human issues, particularly non-technical humans are also at hand.
And until there is stability and a language people don't feel is a moving target to learn, that will remain the chicken and egg scenario.
Rust is trying to solve a set of problems, some combination of which exist in pretty much every programming language that exists today (including Rust). "Have you considered giving up?" is obviously not a very interesting question and not one that's going to get much traction.
> it just clarifies edge cases, and does not change downstream C code in meaningful ways.
This is exactly the same as Rust. (Again, with the small exception of soundness fixes, which, depending on impact, we sometimes leave a year of warnings in before actually changing.)
My argument is not that they are comparable. My argument is that it's a difference of degree, not of kind.
Of the first 15 or so Rust releases, 4 of them caused library code I maintain (timely dataflow) to publicly break. They were all minor "paper cut" breaks that one could fix by changing the syntax, but several were in the public APIs (most were around convenience features, clashing in namespaces). The stated position of the Rust team was that it is ok to break code in this way.
I brought this up with some of the team; I do think they are more aware of it now; many are still more excited to land new things than preserve the experience of the users (I'm ok with that, but it is what it is).
There's a well-known feature in similar languages that would solve at least two of the mentioned cases in one go: https://philipnilsson.github.io/Badness10k/escaping-hell-wit...
Thank you for the link, I enjoyed it a great deal!
Haskell currently uses a garbage collector, as do most languages that we could look to for inspiration, but I don't see that that's an essential barrier. The part I would be worried about doing in the presence of a borrow checker would be being able to form first-class functions, especially closures, but Rust already has those! AFAICS the only other "hard" part is having higher-kinded types and preserving good type inference, but the solution to that is already known (Hindley-Milner).
It already has monads, and uses them in a lot of places. Do notation is just syntax sugar.
When I've seen discussions of HKT in Rust it feels like there's an awkward circularity where if you talk about HKT people say the use cases aren't there, but if you talk about things that need HKT people say they can't be implemented because they need HKT. And I'm worried that the conversation about extending the type system has reached a place where adding ad-hoc special cases is seen as the "cautious" approach, when the result is a funny subset of a more general system that's both more complex and less powerful than biting the bullet and implementing the general system.
I don't think that anyone who understands this stuff deeply denies that there's good use cases for HKT. There's just not a design, and we're not sure that one actually exists. And a general perception that GATs will give us most of the big benefits of HKT in a way that feels more Rust-like. That being said, there have been a few attempts at this. We'll see.
The choice to focus on GATs is exactly what I was thinking of. I think Rust will come to regret it, as it comes with most of the costs of full HKT but leads one into working with a limited subset that's not so theoretically well-founded, and I'm not convinced the forward-compatibility is as good as claimed (the semantics may extend to full HKT, but it pushes syntax in a direction that seems likely to make for a frustrating syntax for full HKT). I hope it ends up working out.
I can see how maybe working toward that would be a priority in a PLT research sense, but Rust isn't that kind of language anymore. (This is my own opinion and I'm not on the lang team, mind you.)
I'm not aware of their having been studied to the same extent, so I don't have the same level of confidence that they are well-behaved. For example, is there a known algorithm for doing perfect type inference in the presence of GAT? What about when we add in subtyping, or higher-rank types? Maybe it's all going to be fine, but the further you stray from the path that's been trodden by PLT research, the more I'd worry about falling down a hole.
Rust is … just Rust.
* .await. This feature was in development for almost five years. Landed November 2019. This is a legit new feature, with significant ramifications on the language.
* try!() -> ? . This feature was in development for two years, and landed in November 2016. This largely consists of the transformation "try!(x)" to "x?".
* impl trait: this feature was in development for two years, and landed in May of 2018. This has two major parts: one that is very rarely used that is mostly sugar, and another that's pretty useful, and is a legit new feature.
* dyn: this feature was in development for two years, and landed in June of 2018. This is largely optionally adding `dyn` in some places. You don't have to do this. You can turn a lint on that will tell you exactly where to do this, and it can be automatically added to your code.
So that's four features. One of them three years ago, two two years ago, and one last year.
I don't get why people complain. C# is pumping features at a much higher rate and most people are glad. Those features do solve some issues or make it easier to solve them. If you don't have an use case for them, then you don't need to learn them.
In C++, using modern style is .a nicer experience, but at the cost of having to stay away from any code pre-C++11. This is not because C++ designers are bad, but rather because they have more cruft due to existing for longer. Time will tell if Rust will go down that same road, but right now new features fit with the rest of the language and are usually extensions to things that already exist but that weren't allowed in places.
I mean, sure, that's an opinion thing, and obviously you don't share it. But to me, watching from outside the core rust community, it sure seems like exactly the same thing that happened to C++.
The fact that it's controversial and "doesn't fit with the rest of the language" means it will very likely never become an RFC in its current state, much less become part of the language.
There is no need for the author to make constant changes to his code. It will continue compiling just fine. Moreover, new "editions" come along with nice documentation on what changed and tooling to automatically fixup code, and are totally opt-in. You can simply wait for a new edition to come along to update your code -- or not, it doesn't ultimately matter that much.
So, there has been 1 new edition since 1.0, and, as the parent comment says, the only really groundbreaking change since 1.0 is the stabilization of async support.
Other changes, such as the `dyn` keyword and match ergonomics are mostly superficial, and things such as NLL and additional lifetime elisions are just simple quality of life improvements that don't fundamentally change the language.
In general, old Rust code continues to work almost completely unchanged. There have been 3 or 4 cases where I couldn't compile an ancient dependency because it was relying on something weird like a soundness bug. The fix was usually to update the dependency to the latest minor revision.
Rust 2018 fixed a few minor syntactic issues, adding the "crate" keyword for imports from the current crate, and "dyn" in front of dynamically dispatched traits. These can both be updated using 'cargo fix' in about 20 minutes. Rust 2018 modules can use Rust 2015 modules almost completely seemlessly, even when macros are involved.
Is it perfect? No. But updating older code is something I spend a day or two a year on.
Moreover, in some companies you cannot simply update external dependencies without extra painful reviews.
Sorry, I can't remember the timestamp right now but the entire thing is worth a listen:
I am not sure why people downvoted me.