Oh there is just no way it's harder to learn than C++. C++ has the problem that it's all of C (almost everything from C is still in there) plus all the stuff bolted on to achieve C-with-classes when OOP was the new hotness in the 1980s plus then several further evolutions to add major new features, not always in a very consistent way.
There is a sub-tribe within C++ that wants a simpler "subset of the superset". They believe C++ could be made into a healthy modern language which is practical to teach as a first language to students and still has excellent performance and hardware compatibility. But if you speak to members of that tribe they invariably feel there's just a few little tweaks to make to the superset first... and I just don't think their dream will ever actually come true.
Rust does have a lot of complicated stuff, but almost all of it secretly turns up if you wanted to be good at C++ too.
For example, borrowing versus ownership isn't explicit in C++ but it's necessary to understand what's actually going on so that you'll find Stroustrup has actually written proposals for C++ that formalise this exact notion, they just can't be checked for existing C++ as they are in Rust.
Yes. There is a "c++" which is simple, consistent, and fairly easy to use, but it's buried in the cruft of decades.
The biggest complaint about Rust's "hardness" is ownership and move/borrow mechanics. Modern C++ has std::move, ownership, and lifetime considerations.
Add to that smart pointers, initialization, a bloatastic std lib, an even more bloatastic pseudo-std lib that you need if you have to interop with anything older than 14 (boost), umpteen different build systems, compilers, toolchains, and all that on top of almost-not-quite-C-superset, and you have a massive surface area.
On another HN thread, someone commented that developers took 3x longer to get to speed in a Rust codebase than C++. I say no way, Jose. I was committing PRs in Rust within a few months. I still don't feel comfortable in C++ of any serious complexity. That's with 2 years of C experience, 1 yr of intensive C++, and like, a few months of rust.
Last week it was a combination of forgetting the exact specifics of RVO hitting the implicit copy constructor that was causing things to go sideways. The number of implicit behaviors are pretty wide and things like move semantics still feel pretty brittle.
I had some really mission critical camera driver stuff I was working on and just never felt secure that there was a massive bug lurking, despite code review, Valgrind and testing. But on that same project, I had enough confidence to write a small daemon in Rust, with like...maybe 60h total experience in the language. Huge peace of mind knowing the code does what I expect it to do.
This is mostly true. As FP and Rust have shown us, if you can treat an input data set as immutable, while building up the result data, you have a much easier time. I bring those patterns into my C++, which speaks to your point.
I'd offer one exception though: mutability xor aliasability on a per-object basis is something I wouldn't take to my C++ code. It may be the core of Rust, and it's a neat tradeoff that enables some of its other benefits, but the indirect complexity cost is too high, IME.
> Oh there is just no way it's harder to learn than C++.
I think the jury's still out on this one.
C++ has complexity, but has the benefit of a gradual learning curve. You can start with C, slowly add `virtual`, then add templates, then add the STL.
Rust has just as much complexity, and forces it all on you at once. Because of that, it is harder to learn.
Some people say Rust's rules are intuitive. I have to disagree. These years of working with Rust and teaching Rust to others have made it clear to me: though the basic concepts are simple, the architectural implications and workarounds are not.
I'm not saying C++ or Rust is better. But C++ is slightly easier to learn, IMO.
I commonly hear "But it's hard to learn how to use C++ safely!" and I generally agree with that. ASan has made things much easier in that regard, but C++ will probably never catch up enough for some use cases.
Having seen teams go Java -> C++ and other teams go C -> C++ I don't really think that's the case. The former leans on shared_ptr everywhere, the latter ends up hitting footguns with implicit behavior around move semantics, RAII(seen more than a few use after free here) and the like.
ASAN and other tools help, but assume they are run regularly and kept to a level of rigor I haven't always seen in codebases.
In both cases, one benefits from input from those experienced in the language.
(not saying c/c++ is better than rust, just commenting on the difficulty)
With C++ unless you have someone experienced on a team there's a host of "unknown unknowns" that feel like the team is making progress but then having to pay the cost when it blows up and usually when schedule pressure is near the highest point. Same for the build systems, retrofitting LTO is not a fun experience.
Rust in contrast calls out many of those things upfront, I've found those guardrails prompt conversations in tradeoffs and conscious choices(to say use Rc/RefCell) you'd be making anyway in C++.
I'm not attacking rust, btw. We all know c/c++ isn't great. Theres plenty of aspects where rust is better, and plenty where c++ is better.
But we're just talking about difficulty, and in this context it's fair to say rust isn't necessarily better than c/c++.
Language difficulty I'm having trouble seeing how C++ isn't harder there. You have decades of features, lots of gotchas that I could rattle off and a bunch of implicit behavior that you only learn about when it blows up at runtime(if your lucky). Heck, I've seen heap corruptions stay latent in codebases for 6+ months only to have a new field added that causes them to cascade into a failure that took 3 weeks to debug across 2 different teams. I've still seen modern compilers not catch basic use after free scenarios in code today.
I won't disagree that you can get code to compile quicker in C++ but if your goal is feature velocity you're better served by a high level language. If your goal is stability, performance and memory which is the domain of systems programming then I think C++ has a bunch of hidden failure modes that Rust frontloads in an elegant way.
There are projects where you'd pick C++ for portability on platforms LLVM doesn't support or momentum with existing codebases but if I had to pick between the two my personal bias is towards Rust for all the reasons above.
This is an unsubstantiated claim. If Rust makes something hard, that doesn't automatically make it universally bad. Nearly every large Rust code base I've seen uses reference counting (both `Rc` and `Arc`) a lot. Moreover, it is not practical to write general-purpose code avoiding both GC and RC because the need for shared ownership will inevitably crop up at some point. So ruling it out as code smell is a bit dogmatic.
It is also worth noting that ownership semantics is one approach (among others like GC) to memory management. In future, more languages will have some sort of ownership semantics along with GC. Nim's ORC is currently a good example.
Nobody here is saying c/c++ or rust is better after their respective learning curves. No need to be defensive, they're just languages, and every language has its strengths and weaknesses.
To the point: modern c++ is easier to learn nowadays. shared_ptr and unique_ptr are as easy as Rc and Box, but without the borrow checker making things harder. One doesn't have to deal with the decades of difficulties when learning c/c++. One can choose to dive into a terrible legacy c++ project just like they can dive into an unsafe mess like Actix, but thats hardly a reflection on how easy to learn a language, just the codebase you're learning from.
And rust's front-loading of problems is a great thing! It also makes it harder to learn, which is the point here.
I would argue that if you don't understand the ownership of your data you don't really understand C++(or C) and you're still learning the language. That's usually the big shift I see when developers who have a background in higher level languages moving down to C++. I've seen plenty use after free and heap corruption happen in C++0x11 and onward codebases both staying completely within modern best practices and when code diverges since there's no guardrails(ex: std::string::c_str, std::unique_ptr::get and the like).
Once you include all the footguns that exist(yes, even in modern C++) and the legacy parts of existing codebases or code that diverges from modern C++ that's why I think it has a much higher learning curve to be producing code that is at the same quality as a passing compile from Rust in aggregate.
If one is just designing a game, or a command line tool for internal use, one needn't make it completely rock solid, and C++ has the better learning curve.
If one includes the requirement to make it safer, Rust has the better learning curve.
And then if someone actually values memory safety, they use a GC'd language and laugh at how many engineer-months and proofs it will take us to make code as safe as theirs. Sigh, such is life...
Rust has "interior" mutability for when you need mutable access to shared data. It works very similar to the "mutable" keyword in C++ except that it's always type-driven, as opposed to something being specified as part of a declaration.
But then, once your code has RefCell<> everywhere, you're incurring runtime overhead, at which point one might as well switch to a language that makes those tradeoffs easier to wield.
This'll get even better in the near future; a lot of languages are improving in this regard (Cone, Lobster, etc).
The runtime overhead is quite trivial (a single word per object IIRC, which is accessed w/ simple increments/decrements/checks as needed) and work is ongoing on abstractions that don't incur any hidden overhead (called 'GhostCell').
If it's a rather short-lived object and we don't need to reuse its location, it can be in a Vec, which involves bounds checking and O(logN) calls to malloc, which is not amortized. Sometimes we can put short-lived objects in an arena, which is the best option, but they are very memory hungry, which has its own costs.
If it's longer lived, we can use a Vec with indices or a type-pool, but we risk use-after-"release", which can be a privacy risk and defeats some of the nice "sanity" properties of Rust code (this is still a good option in some cases, IMO).
If it's longer lived, and we don't want those drawbacks, we have to fall back on something like Rc or generational indices, which both have their runtime costs.
IME, only a very specific kind of use case can avoid any of this runtime overhead. When you get to the more complex use cases, especially with lots of unavoidable state, these overheads appear more and more, and make the whole situation a little less clear-cut.
(This was a rather hand-wavy explanation, I can make it more accurate if you'd like)
We probably can't agree on which half. :-)
No, not literally.
I would say the biggest barrier is developers not wanting to jump on yet another "this language will solve all your problems, trust me" train.
My experience in my 15 years in software, is that good developers solve problems, not languages or tools.
... a programming language designer should be responsible for the mistakes that are made by the programmers using the language.
[...]
It's very easy to persuade the customers of your language that everything that goes wrong is their fault and not yours. I rejected that...
http://blog.mattcallanan.net/2010/09/tony-hoare-billion-doll...If anyone is unfamiliar with Tony Hoare he's the creator of quicksort, Hoare Logic, CSP, worked on Algol, created null, holds a turing award, and much more.
At 15 years I thought the same. At 25 years I have come to really appreciate some tools ability to help.
There are a lot of tools that claim to help but don't. Beware of tools that try to manage complexity - what you probably need is to reduce complexity. I'm looking at Eclipse and Matlab in particular, and anything similar (think enterprise software).
Git - awesome. Using it on 1-man projects too.
Sanitizers for C++ I've seen these identify bugs that hadn't manifested visibly yet.
Rust - well I want to write more because it doesnt require additional tooling to write correct code. Remember reducing complexity.
>> At 25 years I have come to really appreciate some tools ability to help.
I've come to find there is a bidirectional connection between good developers and their tools. Good devs recognize and gravitate towards systems that help them thrive, and away from painful ones. Good tools draw in good devs, and the community gets stronger as a result.
IMHO, One hallmark of less-than-stellar devs is a tendency to stick with tools they are familiar with, because "it works." Great devs could get by with a magnetized needle and a steady hand, but they know there is better out there.
Never underestimate what a team of overly enthusiastic junior engineers are capable of :-)
Meanwhile the junior-intermediate devs spend most of their working hours writing code, so they are extremely sensitive to the 5-10% productivity bump that could be gotten by using {nicer language, better build system, static analysis tooling/sanitizers, ${TOOLING_IMPROVEMENT}.
I'm talking real advances in productivity. Imagine working on a C++98 codebase today in 2021 because you're working on a very large industrial product that's been around for decades. It can be demoralizing, like having to write COBOL or FORTRAN.
Package managers, which ones?
Perl was the very first, almost every language has their own, and even the growing C++ ones are able to use binary libraries on their package managers.
In terms of influences, here's an OCaml attempt to replicate cargo (I wish them well): https://github.com/OCamlPro/drom
Klabnik also mentioned Python's poetry below. Yarn as well, since Yehuda Katz was involved.
Note: I seem to recall you're an Ada dev, so I should mention that as far as build tools go, I've always really appreciated gprbuild, which is far superior to other build tools of its vintage. If it had a built-in package manager like cargo, it could compete with cargo.
I like Ada, but no not an Ada dev, rather JVM/.NET/C++, I rather go polyglot.
And in this regard, while cargo is nice, I don't see how it is better than Maven (I don't suffer XML allergy), NuGET, vcpkg/conan, specially because it only does source packages with npm like levels of dependencies.
Anyway, yeah, Maven is very good and competes with cargo, as well. I agree.
Nuget? Absolutely not, based on my previously undefined rubric. Unlike Maven and cargo, it's just a package manager unless it's recently been expanded (I'm not much of a Windows guy, so not sure). With some pretty crappy defaults as of a year or two ago. No idea about vcpkg or conan.
(I probably should have specified that I'm comparing among package manager that include build tools - or vice versa).
Edit: I suspect cargo will get around to binary packages at some point, as soon as that limitation is resolved in rustc using something like TUF. cargo has not been around nearly as long as Maven.
And for the record, despite my love for functional programming, I also have a strong appreciation for Java (not as much as Ada, but it's a great language when used by competent devs).
Python's Poetry comes to mind.
>> thread-safety
In 2021 what web development stack is making you write threaded code?
>> correctness that matters
The compilers guarantee end at the edge of your processes address space. You still have the same big problems as everyone else:
1. Are you building the right thing?
2. Does everyone understand the domain adequately to do their job?
3. …
101. Do i have a race condition
Except you have a self-inflicted problem - you’re slower than a dev team working in a traditional language (java, go, python, …) and you can’t easily hire talent.If anything my experience is that it's quite productive to work in Rust. For example, compared to Python (which you mention) we catch bugs earlier (with the compiler) rather than later (system test) and so development is faster. Shift left.
It's particularly important for races because they are some of the most difficult bugs to find and fix.
I'm radically more productive with Rust vs Python (having been using both for >5 years). Our codebase is split between the two and I'm working to move virtually all Python code to Rust because it's become such a burden.
* IDE experience is considerably worse in Python due to lack of types (we use type hints aggressively, but that only helps so much and not at all for 3rd party libs).
* Working with JSON is considerably worse in Python in my experience.
* Package management and building is considerably worse in Python. It's still difficult to manage a many-package project and get what Rust provides by default, like workspaces, cryptographic lockfiles, etc.
* Error handling and other patterns in Rust make it far easier to write in a robust way or otherwise correct in the future. Rust is far more resilient to errors during refactoring - I'm very afraid to touch some of our Python code, by contrast.
* Mypy is nowhere near as good as rustc. Mypy errors are traditional "expected blah got blah", or sometimes "expected List[T] got List[<nothing>]" and other unhelpful errors. Rust errors are almost always expressive and helpful - often telling you exactly how to fix your code (to an insane extent - like using a heuristic to tell you where you probably forgot a curly brace).
Mypy is impossible to use with the strictest settings. Lack of recursive types makes working with JSON blobs impossible, makes it impossible to do circular generics (you can't do what Rust does with From/Into as far as I know because of this).
And then there's shit like circular imports being a runtime error, and other little papercuts in the language that are just littered everywhere.
I actually really like Python a lot, despite all of this, but I'll never choose it for web services again.
And hiring hasn't been a problem at all at my company, nor was it at Dropbox where I worked previously
Go? I've had thread related bugs in go related to serving our UI.
Generally any reasonably efficient stack where you need to pull on more than 1 CPUs worth of resources to quickly serve a request. And most stacks where you need to make multiple requests to other services simultaneously to quickly serve the original request.
The framework might not make you but that doesn’t mean your code won’t need such things.
Go uses "green" (user-level) threads.
We evaluated several frameworks and liked the model that Actix has and also its relative maturity (for example documentation / examples). It has been pretty straightforward to work with - including doing things like custom authentication and REST endpoints that dynamically exist.
For the drama, I guess you are talking about the situation that used to exist where Actix had hundreds of unsafe blocks. That's pretty much resolved now, with perhaps a handful of performance critical unsafe blocks left.
I'm not passing any judgements, I just know that people were somewhat worried about the future of the project after its creator very publicly washed their hands of it. Sounds like it's still going, which is good to hear
Then they really should think about changing their name, seeing as actix the broader low-level framework is supposed to be all about Actors.
But this is backend web development, the major languages are more like ruby/php/python/js/java. You might write a C++ extension as an endgame move, but for webdev it's never an opening move.
What was the process of proposing rust and getting buy-in like?
We've actually bootstrapped relatively easily. We have a some developers who have picked it up quite easily, perhaps with some pairing. We run an hour session every week where all the Rust developers get together to share production code and present on Rust topics. The discussion focusing on Rust production code has been particularly helpful.
I started learning Rust and C++ at the same time, coming from a world of Java, Python, and Ruby.
If I need to solve a problem in C++, I would much rather write Rust because the compiler actively screams at you when you do bad things.
And in my opinion, the challenging part is learning what those bad things are. In other languages, the compiler doesn’t care. You immediately see your program crash or things fail in subtle, hard to reproduce ways later in production. Rust changes that model.
I disagree with you on the documentation. I think the Rust documentation is superb. I have yet to see any language give nearly as meaningful information when your code fails to compile. Secondly, Rust front loads and throws issues immediately in your face, as opposed to having you discover problems in your code at runtime. I don’t think that makes Rust difficult - it’s forcing you to think about your choices. And particularly it’s making you think about concepts that perhaps you completely overlooked in other languages - thinking about stack vs heap allocation, moving semantics, shared ownership, etc.
If you want to quickly prototype something, Rust isn’t the best language IMO. But if I have a choice between C++ and Rust, I’d go with Rust anyday. If I was starting a new project from scratch and it was more of a web service, I might go with another language over Rust only because I’m more of an expert in Java, Python, etc. and it’s easier to hire developers for those languages than Rust.
But I’m biased. Rust has a special place in my heart. It’s the first language I’ve learned in many years where I felt some sense of accomplishment and did not at all feel like “Oh great, another tool that fundamentally doesn’t add value.”
For my use cases involving Java and iOS interoperability in a multi threaded environment, nothing else came even close.
My eyes can’t possibly rollback far enough.
I don't think rolling one's eyes is the appropriate reaction when someone pushes for it. Depending on your requirements, there are definitely cases when rust is the best choice for a web backend. Especially when moving fast and breaking things is not an option, such as in the author's case.
We will also run a training course next month just based on the Rust book plus some workshop examples.
Does Go offer similar guarantees for concurrency?
For races related to external resources, or caused by misuse of OS IPC mechanisms (which is the future thanks Spectre), it offers as much protection as any other language.
I see a big Rust benefit when you’re writing systems level code or you’re writing once and use the code as a library on Android and iOS.
But what type of logic are you writing in your services that requires Rust’s safety guarantees? Or is it that you’ve found you can respond to the same performance metrics (total requests per second) with fewer hosts because there’s no JVM or garbage collection overhead?
For us, the safety is the critical reason to choose Rust - particularly the thread-safety. Also the relatively small memory footprint compared to something like Java. Performance hasn't driven our decision at all - the number of requests per second is very low. It's correctness that matters.
We are a bit unusual because customers have locally deployed servers rather than servers managed by us, so bugs are harder to fix.
We haven't hired any developers specifically for their Rust experience, but have instead trained internally and now have a group of about 10 Rust developers working across a couple of projects. I would say that training like this has been relatively straightforward. One of our hires, a recent graduate, had some existing Rust experience with personal projects, and this was particularly beneficial in helping the team to bootstrap.
The ecosystem is surprisingly mature. Async is probably still shaking out somewhat, but that's moved on hugely in the last couple of years, and we haven't had any particular problems.
Overall the experience has been hugely positive.
The biggest barrier to adoption, in my opinion, is that people are scared of learning Rust because it has a reputation as being difficult. I don't think it's any harder to master than some other major languages such as C++ or Java - although perhaps you have to understand more at the beginning: ownership and borrowing are right in your face.