Fully agree that the language is very taxing on the braincells and I think this is the main reason why Senior C++ Wizards tend to hate Rust. Imagine spending 10 years learning the little details about lifetimes, thread safety, million different UB cases etc. and then seeing a kid fresh out of college fearlessly writing performant code by leveraging the amazing Rust compiler. I bet it must be an infuriating feeling which is why I'm glad that I bailed out on C++ early in my carreer.
> seeing a kid fresh out of college fearlessly writing performant code by leveraging the amazing Rust compiler
A nice (and very ridiculous) fantasy, but no.
As a person who hires developers to write performant code, I'd love it if there were these magical junior Rust developers who can write performant code.
But as the market exists right now, Rust developers are twice as expensive and the product they deliver is twice as bad.
The only way I can justify Rust to higher management is selling it as a vanity PR project.
The main point of Rust is correctness, not performance. Performant code is a nice to have - but when the two are in conflict, correct code wins every time. How can one ever be confident that your Senior C++ devs are writing code that is free of subtle bugs? Are you going to look for professional Haskell or Ocaml devs too, for a proper comparison? Those folks ain't cheap, either.
I maintain large scale C++ code that hasn’t had a bug in production for 5+ years. I do it using lots of system level tests. So it is very much doable. It has nothing to do with the programming language. Yes some languages (Haskell for example) completely eliminate a large class of bugs, and I would prefer to use languages like that, however the world runs on C/C++. So having solid C++ skills is great for your career.
> But as the market exists right now, Rust developers are twice as expensive and the product they deliver is twice as bad.
What? Why? I've been developing C++ for 10 years and seen firsthand the problems it causes and that are addressed by Rust. If I start writing Rust I'm going to be more productive (from build system to maintenance), write more performant and reliable code faster, so I don't see how the delivered product would be worse, as I'd have more time to iterate.
About the rate, maybe that would be more expensive, but I'd say it's still worth it. Anyway, it's not like getting competent C++ developers is cheap either.
As for GP's message, I agree it looks unrealistic that "kid fresh out of college" would magically write performant code because they're writing rust. There is more to performance than the language, and it is particularly easy to write bad performance code in rust (and in C++ I must add). That being said, I'd still be less anxious to have a junior in a rust codebase than near a C++ one.
> If I start writing Rust I'm going to be more productive (from build system to maintenance), write more performant and reliable code faster, so I don't see how the delivered product would be worse, as I'd have more time to iterate.
Maybe it's true for you, but I can hire a competent C++ programmer to deliver code faster for less than what you'd ask.
Also, if a programmer is spending time "iterating" on a performance problem, he's already underperforming. That's okay for less experienced developers as part of the learning process, but expecting it to be the norm? No.
> About the rate, maybe that would be more expensive, but I'd say it's still worth it.
> Also, if a programmer is spending time "iterating" on a performance problem, he's already underperforming. That's okay for less experienced developers as part of the learning process, but expecting it to be the norm? No.
I don't think the OP meant iterating on performance problems specifically, just generally iterating on code as one does when they write it.
Also, every programmer at some point iterates about performance problems, not matter their experience, or language (or implementation), or runtime that's being used.
I think the original "uni grad can write better Rust code than experience C++ dev can write C++" is a hyperbole. I don't think they meant better, but the Rust developer will need less context to implement the same feature.
The spirit of that sentence was probably "it's simpler to write performant and safe code in Rust than C++". But again, without really agreeing what "performance" means here, it's a bit silly to discuss.
Performance is such a complex that can mean so many things depending on the problem. So, I we're all nit-picking about something without even agreeing what they're specifically nit-picking about.
The default IO mode in C/C++ is buffered. Rust requires you to wrap it in `BufWriter`. For most network (or general IO, really) workloads you want buffered IO.
So sure: We could say that writing performant IO code in Rust is more difficult, if we hyper-focus on this one specific detail.
FWIW (and people who're unfamiliar with this): I think switching to buffered IO in Rust is absolutely trivial, and very well documented (in multiple places where it's relevant). In fact more so than it is to switch to unbuffered IO in C++, because you have to call `setbuf` with "special" values to disable buffering.
We can then argue that experience/wisdom diminishes most of these arguments about performance, but it diminishes them on both sides.
> I don't think the OP meant iterating on performance problems specifically, just generally iterating on code as one does when they write it. Also, every programmer at some point iterates about performance problems, not matter their experience, or language (or implementation), or runtime that's being used.
Exactly, on both accounts, thanks.
It strikes me as odd that GP would describe "iterating on performance" as "underperforming", unless GP is in a very specific domain where the performance requirements are always known in advance and where they never change. Getting the last drop of performance from code is a trade-off. It impacts the readability and maintainability of code. To get the last drop, it can depend on the shape of your expected input data, and it requires looking at the produced assembly and performance profiling results to find bottlenecks and act on them. This alone implies iteration. If you're telling me that your engineers know their compiler so well that they can tell in advance what assembly code will be generated when changing seemingly unrelated parts of the code, eg they know the inlining limits of their compiler, I call BS.
Iterating on performance takes time to develop and maintain, which is why it should be done just enough to meet the requirements, in particular in the hot loop and to reduce bottlenecks. If your developers are optimizing all the code they write to the ASM instruction, they are probably underperforming (unless specific domain where so little code is written or the target is so small that the benefit is worth the cost). Understanding that trade-off is part of seniority (aka juniors will often both "prematurely pessimize" by writing inefficient code without increasing maintenability or in the hot loop and "prematurely optimize" by avoiding allocation at all costs in CLI argument parsing).
I measured it. On multiple projects. Some I started writing in Rust, so this is not the "rewrite effect" at play. I observe x2 to x3 on development time alone, long term if we compound review, debugging and maintenance I wouldn't be surprised to see if it tends to an order of magnitude of difference.
Results will depend on the available ecosystem for each language for the task at hand. For instance I'd still choose C++ with qt for writing a GUI today.
The reasons why Rust is more productive are easy to understand and touch most of the development steps:
- building is trivial in Rust, adding dependencies also. Depending on how broken the CMake of your C++ dependency is, it can take several days to wrap it so it can be used sanely. The remaining time is dependency review, which must also be taken in C++.
- Development itself is much easier, thanks to Rust's expressive typesystem (mostly sum types and traits) and "functional-lite" iterators (C++ is getting there with ranges but today we're still far from the same ergonomy, we're still missing pattern matching, and views introduce lifetime issues). The sane error/warning demarcation allow to develop without the equivalent of "-WError" (which is hard to do sanely in C++ due to eg missing return being a warning), which allows to use these warnings to drive development (you can stub every function and when you no longer have any "unused variable/unused function" warning, you're done). More generally, Rust removes most of the boilerplate of C++: you don't need to maintain superfluous representations of your interface as headers (the documented interfaces are generated using cargo doc), you can derive `Hash` instead of opening the std namespace, functions and structs definition all start with a keyword so they're easy to grep for, the language being expression oriented means we don't have to rely on immediately invoked lambdas as soon as what we want is to bind a variable depending on an if, we don't have pure stupid boilerplate like the rule of five to enforce, and a thousand of other ergonomic improvements over C++. Rust feels like a language designed by a C++ developer tired of the flaws of C++.
- Since many errors are lifted at compile time, the feedback loop becomes much shorter. On the same library that has both a C++ and a Rust interface, we had memory errors several times due to a function that returns a view of its argument without that being clearly expressed or expected. In C++, this meant looking for what went wrong after a segfault, something that took an hour to two engineers. In Rust, the compiler told us that the particular variable was not living long enough, and went so far as to suggest the fix. Time spent on this: 10 seconds. And this comparison only hold for the memory errors that we did catch in C++...
- Test and documentation use standard tooling that is included by default. The ecosystem documentation is stored on a centralized website, meaning is a single place to look at to find documentation (that at least contains the interface of the library).
- Review is made easier with no implicit code paths due to implicit conversions or exceptions. Error handling has the right amount of noise, a single character to pass back the error (possibly adding some context), otherwise a match construct to handle each error case. The error story is not perfect, but much better than in C++. The "dangerous patterns" are made easy to spot (unsafe, unwrap, etc) and should be confined to specific parts of the code (eg hot loops where benchmarks prove that it is necessary). Rust make code review able to focus on the problem at hand rather than the many rules you have to follow to not blow your foot off in C++. In the presence of other developers (or even for myself :-)), it is really welcome.
- Maintenance time is reduced by all of the above, over time, compounded by the ease with which one can refactor Rust code compared to C++ code. If refactoring a module without unsafe there is no chance of introducing memory issues. Generally the strong type system (no implicit conversions) makes it easier to not accidentally introduce logic errors
Now, there are drawbacks to Rust: the ease of adding dependencies means that we can have deeper dependency tree, so this make reviewing dependencies a bit more tedious (thankfully code review is easier). Occasionally the borrow checker comes in and forbid some patterns (mostly when we would write code depending on iterators not being invalidated in C++), and having done less rust than C++ sometimes I need to find the correct pattern for something. On average though, the advantage is very clear.
Which is it? Barely a day ago, you said "If I start writing Rust..." Now you claim you have been writing Rust all along, on "multiple projects", keeping measurements. Forgive my skepticism.
Sorry for not being clear. Should have written "when I start writing Rust" at this point. I have been using Rust personally since 2016, and professionally since 2019. Most of our codebase is naturally still in C++ (we're not aiming at "rewriting the world"), so I can frequently feel the difference between the two languages, which is why my preference is so clear-cut.
> But as the market exists right now, Rust developers are twice as expensive and the product they deliver is twice as bad.
It's interesting how we have different experiences regarding this. In my experience, it's usually the opposite. And I believe it's mostly because Rust lets you focus more on the problem you're solving, rather than thinking about dos and don'ts of the language.
For example, most Rust projects (or products) seem to have much more polish than equivalent C++ projects (even for ones with years head start). By polish I mean both user experience and the developer experience when one works on the project.
Also, adding user-facing (i.e. UX) polish to Rust projects requires less ahead-of-time planning in my experience.
One reason is that when introducing new Rust code, there's less performance gotchas, and Rust code seems to compose more ergonomically than C++ code... Most of the time at least; there were times that Rust was frustrating. But I think less often probably, _and_ the frustration is compile-time, rather than runtime. Also Rust has a shorter feedback loop due to Rust's incremental compilation really helps mitigate the frustration, so you can iterate faster.
And another is because I've always felt like refactoring Rust code (even with C++ experience than Rust) tends to be easier, and faster to refactor, and I feel bolder while doing it, if that makes sense? Simply because the compiler thinks about various categories of problem domains instead of me, and it tells me about them.
Of course, it's never black and white with any of the above things I mentioned; the situation is grey in both ecosystems. But I think it's a much lighter shade of grey in the Rust space than in the C++ space. And as always, there's always exceptions on either side, too.
EDIT: I quoted OP's full sentence, but then only addressed the last bit. I hopefully clarified in comments below that I wasn't talking about the first part. Sorry about that!
Obviously your experience doesn't involve budgeting or hiring developers.
Your subjective feelings about how nice or fast it is to write Rust code isn't what we're talking about here.
The original poster claimed that Rust can save money by letting managers hire junior devs to replace senior C++ developers.
This isn't true. Rust developers are much more expensive than C++ developers, and have vastly lower productivity. (No doubt because of their vastly inflated expectations; any Go/Python/PHP developer who upgrades to Rust suddenly thinks they are elite 10x programmers because they have a clue about performance now.)
> The original poster claimed that Rust can save money by letting managers hire junior devs to replace senior C++ developers.
This is not at all what I said, nor meant. I never said anything about the hiring market, but merely stated a personal observation why I think Senior C++ devs often dislike Rust.
Senior C++ developers are not impressed with Rust because it doesn't solve any problem they have. Switching to Rust would make them markedly less productive, with no corresponding benefit. The drain comes from slow compilation and from excess design overhead, the need to distort a simpler architecture to satisfy the borrow checker.
Put simply, the extra work to satisfy the borrow checker prevents errors they do not make.
This contradicts Rust propaganda, where a few CVE numbers are cited over and over, echo-chamber style. What these citations never mention is that they are for bugs in mostly very old code. We just don't need to write code that way anymore. It is not just error-prone, it is more work, so there is simply no temptation to write it that way.
I do hear of programmers who have elected to stop learning, and (proudly!) admit continuing to write C++98 or even "C-like C++". We used to say "you can write FORTRAN in any language"; there is no eliminating bad programmers, but plenty of reasons to avoid their product. What conclusions can we make about Rust from its worst adherents?
> What these citations never mention is that they are for bugs in mostly very old code.
It is unavoidably true that grave security holes get found only in existing code and not hypothetical future code written with a later language revision and whatever new features will definitely avoid those "errors they do not make". That's time's arrow for you.
Can you name a few of these "Senior C++ developers" and point us to, say, a few years of their work?
Is there a specific vintage of C++ where you feel like, at last, unlike C++ 98 you "don't need to write code that way" and so Senior C++ developers won't make these errors? C++ 11 maybe?
> Put simply, the extra work to satisfy the borrow checker prevents errors they do not make.
> This contradicts Rust propaganda, where a few CVE numbers are cited over and over, echo-chamber style. What these citations never mention is that they are for bugs in mostly very old code.
Empirically this is not true. https://twitter.com/LazyFishBarrel/ is not citing "a few CVE numbers over and over"; for example, in browsers, new code is disproportionately responsible for vulnerabilities. I know most of the people at the big tech companies doing the research into where the security problems in major C++ software come from; they're not making the results up.
> Obviously your experience doesn't involve budgeting or hiring developers.
Ah, that is very true! :) I forgot to make clear in my comment that I wasn't referring to that bit at all.
I only participated in the hiring/interview process, but that's as far as my hiring experience goes.
> The original poster claimed that Rust can save money by letting managers hire junior devs to replace senior C++ developers.
Ah, I didn't interpret it that way. I thought the point they wanted to get across was that it's easier to get the same for less—not in terms of monetary cost, but in terms of less mental effort with regards to context/knowledge about the language itself.
But, the hiring point you're addressing can be inferred from their comment, too.
I think I agree with you on that... It probably is "easier" to hire C++ developers, since there's an older and bigger market for them.
I think Rust developers are in an interesting position right now that they're not as abundant as other developers, since Rust is probably still in "early adoption" stage. So they can use that as leverage in salary negotiations, maybe? But it also comes at a cost of a much smaller job market for them, too.
> I think Rust developers are in an interesting position right now that they're not as abundant as other developers, since Rust is probably still in "early adoption" stage. So they can use that as leverage in salary negotiations, maybe? But it also comes at a cost of a much smaller job market for them, too.
No, you are viewing that from a technological perspective - what you assume only holds if "Rust" was a requirement.
If staffing a project is much harder (and maybe more expensive) when using Rust as language, then Rust will not be chosen if C++ is an option.
I'm not sure about that. I've spent 18 years programming in C++, for much of that time in a senior professional capacity and am excited to learn Rust for whatever my next project will be. Most of my colleagues and peers feel the same. I think the person you are characterising is quite rare. I like C++ but don't love all of the baggage and am delighted that things are moving forward.
> I think the person you are characterising is quite rare.
You could be right... I hope you are right, but I'm afraid you're not...
> I'm not sure about that. I've spent 18 years programming in C++, for much of that time in a senior professional capacity and am excited to learn Rust for whatever my next project will be. Most of my colleagues and peers feel the same. [...] I like C++ but don't love all of the baggage and am delighted that things are moving forward.
I don't know what the "split" is regarding mentality, and I've definitely seen a lot of experienced C++ devs in Rust communities, but then there's also a lot of people in "Rust bad"[1] circles (you see them on Reddit and HN). I think it might be an even split (as with most polarised opinions), but I don't think that either sort is rate.
[1]: I don't blame these people, btw. If you're in a senior or leadership position and you choose C++ for a new project, because C++ is your strong suit. Then you read online "C++ bad" comments that glorify Rust. It's hard to not take these things personally, when it might be affecting your livelihood. Especially if semi-technical management "drinks the Rust Kool-Aid" (for a lack of better expression) and grills you over your decision. It's not a very disheartening situation to be in, and that frustration can fester into contempt, and so on.
I think maybe you're paying too much heed to what you see in concentrated and very self selecting online communities. A lot of professional C++ programmers are more focussed on particular industries, and the domain problems to solve and less so on the language or specific tools. It just so happens that C++ has been the only game in town in a lot of industries until quite recently. At work we've been looking into trying out Rust in some modules where it would make sense. There has been zero objection based on it being Rust, from a team of senior C++ devs. Quite the opposite, most are keen to try it out. But those people don't spent their time on language message boards. Sure, you will find the type of person that you describe. They definitely exist. But it's not the typical case of senior C++ developer, in my experience. Most of us are domain experts in something (automotive, security, medical devices, games, OSes, networking, etc...) and the language comes secondary. If we can better solve our primary goals with a different language then we'd be fools not to have an open mind.
Senior C++ developer here and I totally agree. Anything that can improve productivity/quality while maintaining high performance is very much welcome. Obviously!
In my experience, the problem with juniors and performant code has little to do with lifetimes. The issues I see are as easy in C#, Java, or Rust as C++ - e.g. repeatedly using string appends rather than building up a list and doing a single allocation, or holding a lock for the duration of a reload and parse instead of just the reference swap, etc. Thread safety, more often than not, isn't that hard - just do the work on one thread (I work on a web service with thread-per-request).
I'm optimistic about Rust but realistic (I think) about how much inertia the common project (C++) really has. I'm also decidedly skeptical about Rust's cure-all possibilities - while I've seen a dozen or so real lifetime bugs in a decade, about half of them were caused by a developer misusing an API, deliberately calling a method that is for transferring lifetimes, and then not assigning the reference to a new object.
In my experience, most bugs are just badly written code, not the sort of problem your compiler can fix.
> repeatedly using string appends rather than building up a list and doing a single allocation
I think this "make a list" idea performs best where you know exactly how many things will be in the "list" but have little idea how long the concatenated String might be, so you can keep the "list" as a local array (no allocation) and do a single String::with_capacity (one allocation) and then push all the references onto it.
Otherwise, obviously we're not going to go around assembling a LinkedList since this is the 21st century and we're not crazy, so let's assume if we don't know how long that "list" is we're accumulating references in a Vec<&str>. We don't know how big to make the Vec either, and so that will grow as we push items onto it.
Rust's Strings are - under the hood - basically a Vec<u8> that promises to be valid UTF-8. So we're playing with the exact same allocation algorithm and same amortized costs in String and Vec<&str>.
If we're appending very few Strings in the typical case, the "list" approach costs extra because we're doing a bunch more allocations + copies to make the Vec<&str> that we're throwing away shortly afterwards. Likewise if the strings are very short.
And after all that it's more complicated, which counts against it unless performance was critical.
If I saw a junior reflexively doing this (with a Vec or some other "list" structure) in code I was reviewing, I'd ask them why. If their answer is "performance" I'd remind them: If we aren't measuring it then it isn't performance it's just wanking. So, where's the measurement?
Depends on your string really. If your string are immutable (as in C#), it's not the same amortized cost and you end up wanting to use a StringBuilder almost every time. It's not premature optimization - it's just the standard pattern for performant coding.
But I see your point - I inadvertently gave an example that is performance basics in some languages and fine tuning in others.
The lock example is perhaps more relevant.
Another good example is appropriate caching. Often services want to parse something (e.g. an ini file) and cache it in memory. Caching the right (fully parsed) format will lead to a highly performant configuration system - taking less than 1% of CPU time - but let some juniors do parsing on top of the caching layer rather than underneath your caching layer - and suddenly your config system is 5-10% or your service CPU and is a target for optimization. Most of my daily work in growing juniors is patterns like this, nothing a language would help with.
They hate everything that takes 1us from them, following on the C culture.
The nice C++ frameworks from the 90's all used bounds checking enabled by default, what does ISO C++? Bounds checking disabled by default and it is up to the compiler vendors to do as they please, hence FORTIFY and _ITERATOR_DEBUG_LEVEL.
If you read how magnificent C++ is on the HILO tutorial or C++/WinRT ones, it is quite clear how WinDev sees these nasty folks doing stuff in .NET at DevDiv.
Joe Duffy on his Midori talk at RustConf mentions that even with Midori being available to WinDev they were asserting it wasn't possible.
“Wizard” is the right way to put it. There’s a certain kind of “experienced” developer who enjoys being the local guru way too much. Even regardless of technology, their projects are invariably set up in such a way that no one can do anything without them.
C++ seems perfect for these people because there are so many little traps and arbitrary rules to remember. “Ha ha, you wrote for (const auto x: y) instead of for (const auto& x: y)? Stupid fool!”
(Or is it auto&& now? decltype(auto)? I honestly don’t remember and don’t really care.)
I’m not remotely suggesting all or even most C++ experts are like this, but anecdotally, their share is higher than among, say, Python programmers.
It’s not like Rust is particularly easy to learn. On a conceptual level, it’s probably harder than C++ (traits, generics, lifetimes). It’s just so much less arcane.
I sympathise with East Const, but immediately I am just reminded of another problem with C++
The defaults are wrong. Const should have been the default. Whereupon "East Const" doesn't matter, all the variables which are mutated get labelled. Similarly "explicit" should be the default (nobody wants your random single parameter constructor to be implicitly called, stop that) and "override" shouldn't be (If I name my method foo why is it my responsibility to first check whether some other bozo named a method foo so that I don't "override" it ? The compiler can and should tell me this won't end well†)
† Rust brought me around to the idea that some other bozo's quack() method has no business sharing a namespace with mine just because we're both Ducks, if I wanted to override the shared method I'd have re-implemented Duck::quack() not added my own quack() method. But in the C++ world I'll stick to just saying override shouldn't silently be the default.
Right, C++ has CV-qualified member variables†, because of course it does.
I'm not talking about those. I think they plain shouldn't exist, but it's a separate discussion from the discussion about the defaults.
But yes, this means you can't cut-and-paste C code. I appreciate that this is disappointing for the "Actually we already have lots of C++, it's just that it's mostly .c files and doesn't realise it's C++ yet" people. Set your sights higher.
† Yes, this insanity is also available in volatile flavour.
Not having that would mean no access to the Windows SDK, no access to a libc, the macOS frameworks, etc. It's far from negligible. It takes other languages years to wrap the OS APIs correctly, if they even do it.
Why would CV members be insane ? Having const members has been very useful for me when refactoring codebases, to make sure that I knew every place modifying such a member. It has never been a hindrance, just plain positive, and not having it would mean one more rule to remember (and having to explain students why you can write "const int foo;" here and not there)
They actually can't (well, shouldn't) write "const int foo;" as a local variable because the compiler needs to know its value. And that gives the game away.
These are already different cases, yes both the structure member and the local variable are names with types, but they already need to be explained separately.
I have been programming in C++ for a very long time (25+ years career). And no I don’t hate Rust at all. I welcome any attempt to make better, safer language that are still fast. I actually wish that Rust had gone one step further and implemented Dependent Types and Refinement Types. It would allow you to prove code correct in Rust. Which is even better than the current Rust guarantees. Having said that, there are about 5 million C++ users out there writing C++ software every day. So it will take a loooooooooong time before Rust usage catches up. If it ever will.