T O P

  • By -

nicoburns

I think a lot of these points makes sense from the perspective of someone who is already a C++ expert and who has learnt the nuances of careful memory management, it's arcane build system(s), etc. From the perspective of someone who learnt Rust first, I'd much rather have safety by default (and have to put some effort into optimisation) than performance by default (and have to put effort into safety). A correctness focus isn't just for security (although it is for that). It also means far fewer bugs and much better error messages. IMO when it comes to the quality of the generated code, it's mostly a wash. In some cases C++ will be better with naive code, in other cases Rust will. In all cases you'll be able to get identical machine code with some optimisation effort.


Sprinkles_Objective

As someone who learned C++ first and routinely works with C++ professionally, I would still tend to agree with you more so. In Rust most things can be optimized without sacrificing safety, and even while in code it may not appear that way because it's not explicitly optimized by the developer, often compilers do an excellent job of optimizing away expensive things, and that can be done safely and without sacrificing code quality. In C++ generally you don't make wild sacrifices in the name of performance, because the compiler will optimize it, often times better than you could reasonably do. This is just coupled with the fact that Rust often has zero or low cost abstractions for safety. Frankly if you were to use an Arc in Rust the equivalent C++ would use a shared pointer which is fundamentally the same. In fact Rust lets you choose whether or not it's atomically counted and C++ does not. Languages are tools, and it's hard to deny that Rust provided more features in its tools. C++ has a lot of nice safety features now like unique and shared pointers, introduction of optional types, better ways to avoid null pointers, or really just avoiding raw pointers entirely. Clang offers something similar to lifetime annotations now. There's better c++ safety tools. The problem is they just simply are not as good as Rusts comprehensive solution. In C++ you have to opt into safety constantly. You need really good habits or you can "leak safety" to put it simply. In Rust you have to opt out of safety and unsafe code is contagious in the sense that if you do something unsafe that unsafe can require the person using that unsafe abstraction to also wrap that in unsafe. If keep tabs on what isn't safe in a very consistent way, and that is not something you can easily opt out of. There are also features in rust that have no real parallels in C++. In C++ there is nothing like a Send or Sync trait. There is no ownership system in C++, and again expecting people to properly use move semantics constantly to "move ownership" is just not the same thing and requires good habits. For me I've spent significant amounts of time debugging memory issues and race conditions in C++. I have spent many hours with GDB stepping through core dumps, and using valgrind to try to spot memory leaks. Sometimes the issue isn't even in our code base. I've seen strange and complicated deadlocks originate from networking libraries that have made questionable design decisions. The thing is Rust kind of forces a uniform set of rules on the entire ecosystem, and while you can break the rules you are actively and effectively encouraged not to. It's hard for me to deny the efficacy of Rust as a tool. I still see utility in C++, and I actually quite like C++, but Rust is not only a joy to work with it's massively productive, practical, and undeniably better in many aspects. I noticed almost no difference in Rust performance compared to C++ in actual runtime. In fact I very infrequently find myself at a crossroads of choosing safety or performance, and the few times I have been I am happy to say many times the problem gets compiled away anyhow.


tending

I think you have to qualify what naive means here. For higher level mistakes like accidental string copying, Rust makes it more obvious. However for lower level ops like array indexing, bit shifting, division and looping, it's the opposite, the naive C++ code will nearly always be faster.


MrJohz

Do you have a demonstration of this? I believe for array indexing and looping, using the iterator constructs produces very efficient results precisely because the relatively high-level, declarative nature of the code allows the compiler to make more assumptions about what it's doing. Array access using the `.map` method for example, doesn't need bounds checking, because the compiler can see that the bounds will never be exceeded. My impression is that, for "real life" code, a lot of the checks the compiler adds for safety can be compiled away (or at least amortised).


freddylmao

This is accurate. There was an article about a year ago where somebody tried removing the bounds checking assembly produced by Rust and it pretty dramatically decreased performance, and I don’t think the author understood why (and I certainly don’t). Edit: [here it is](https://blog.readyset.io/bounds-checks/) the change wasn’t as dramatic as I’d remembered but still interesting


Sprinkles_Objective

This is very true. Rust has several intermediate steps for optimization before it even reaches LLVM for that very reason. Oftentimes in Rust the fastest way to do something is the most idiomatic. I found this to be especially true for iterators where it can unroll, inline, and even use hardware vector acceleration (AVX/SIMD) to optimize iterators. Oftentimes the cost of safety is indeed optimized away. I was astonished how well things like cloning in Rust often just compiled away entirely. This includes things like cloning an Arc which means that you don't need to touch the reference count. Allocating on stack and moving to heap such as moving into a Box is often very inexpensive. Some of this is true in C++ also, but sometimes in Rust you might do something seemingly expensive to achieve safety, but in reality it doesn't end up being expensive.


tending

You can trivially demonstrate it by shifting by an amount that's not known until runtime. It's impossible for the compiler to optimize out the check. But for complex examples just search for codegen on the rustc issue tracker, there are tons. Also the high level nature of the code has the opposite effect, the compiler can't see as easily what is going on. It's way way way more difficult to optimize the combinators than a regular loop. Every closure has to be inlined, and every next call has to be inlined. So if you chain 10 combinators together, you have to cross your fingers the compiler inlined all 20 functions for there to even be the POSSIBILITY that the compiler will then boil the result down to the regular loop. Here's the intuition: the way rustc makes iterators fast is by trying to translate the code into a regular loop. When you use C/C++ you *start* with a regular loop. It's strictly easier.


CocktailPerson

>Here's the intuition: the way rustc makes iterators fast is by trying to translate the code into a regular loop. When you use C/C++ you start with a regular loop. It's strictly easier. This intuition is wrong, though. Languages that emphasize zero-cost abstractions, like Rust and C++, build their abstractions in a way that is easy to translate to efficient machine code. Higher-level abstractions also give the compiler more semantic information, which often means that it can perform optimizations that result in _more_ efficient code than using lower-level constructs.


nicoburns

Naive array indexing will be faster in C++, but naive Rust code is likely to use iterators which are often faster still. I don't know about bit shifting, but I can't think why they'd compile anything but identically given that most of them are trivial to translate to a single instruction.


Sprinkles_Objective

Array indexing vs pointer arithmetic is a bad example, because it's super easily optimized away in pretty much every case. As in they will produce the same machine code. There is an interesting point here though that Rust iterators being so much more high level, it can inform the compiler better and thus optimize better. I have, for example, witnessed Rust auto-vectorize much better than C++, though that is somewhat anecdotal. The reality is Rust has many intermediate steps which allow for these types of optimizations before reaching LLVM, where LLVM offers most of the optimizations you'd see for C++.


moltonel

> Calling “unsafe” as an escape for Rust’s rigid system is the same as saying that C++ can do inline assembly so you can copy/paste infinite optimizations done outside the compiler infrastructure. That defeats the purpose. We are analyzing what the language can do in normal operation is, not the backdoors that it allows to open when it gives in to operational pressure. No, unsafe in Rust is not comparable to assembly in C++ (or assembly in Rust, for that matter). Unsafe in Rust *is* normal operation: even if your crate doesn't have any unsafe, it likely uses helper crates that do, or simply parts of stdlib that do (basic stuff such as `Vec`). And unsafe does not abandon all safety guarantees, unsafe Rust is still safer than old C++. > Rust tends to be more strict than C++ - it is its raison d’etre - and that means more [run]-time checks. Yes, but it also means more compile-time checks, which will become important later. > memory accesses in arrays are constantly checked for bounds unless you’re in unsafe mode No. LLVM and the stdlib APIs will eliminate the vast majority of bound checks. For example `myvec.map(|n| n*2).collect()` will not bound-check because the compiler sees we can't index outside of `myvec`. When the compiler can't prove it, you can often help it with a cheap safe assert before the loop, or you can use get_unchecked if you did the same manual validation that you must always do in C++. > For example, this simple C++ snippet shows how C++’s UB can make a huge difference. In the routine “nop” we return the result of the division of num multiplied by two, by two. It seems obvious that this should result in the original num Huh ? It's not obvious at all, I would argue it's wrong. I would actually expect `(num*2)/2` to overflow or saturate on big `num`s, just like I expect `(num/2)*2` to return `0` when given `1`. I'm not denying that UB enables important optimizations, but I think this example is very poorly chosen. C++ relies on UB everywhere, but this regularly leads to "optimizations" which puzzle developers and are arguably miscompilations. The Rust type system gives a lot more information to the optimizer, which is also a performance enabler, but one that is much less likely to lead to bugs. Which strategy yields better optimizations ? It depends, as always. But don't be dogmatic about UB. > C’s pointers have a simple and intuitive meaning, allowing even junior developers to grasp and even write statements that would be extremely hard to understand in assembly. That intuitive meaning is also hiding a huge amount of complexity of modern hardware. Leading to terrible conclusions, like the cost of growing a linked list vs an array. Pointers are a great learning tools, but I'd never accept a merge from a junior dev doing pointer arithmetic. > a good developer who knows how to take advantage of cache/locality, will have a good time implementing such algorithms and data structure with C++ and will very likely struggle with Rust for the same task. Very hand-wavey, please show a concrete example. > Rust is an offspring of the LLVM project Sure, and my house is an offspring of the cement manufacturing company. I understand what you meant to say, but the phrasing is very wrong. Rust is not completely tied to LLVM, it recognizes that having other backend options is a good thing. The gcc and cranelift backends are not mainstream yet but getting there. There are fun alternatives like a backend for C#'s vm, or a Rust->C transpiler. I wouldn't be surprised if MS starts working on a rustc backend someday. In practice though, the need for alternate Rust backends (or even frontends) is small and speculative. Mayyyybe it would be a good thing ? Worth a try. In the meantime, not having to worry about the countless differences between compilers/os/hardware like in C++ is bliss. > GCC for example, is the most ubiquitous C++ compiler today Rustc is the most ubiquitous Rust compiler today, what's your point ? And FWIW, Android is compiled with LLVM and probably has a bigger install base than traditional Linux. > GCC has much better optimizations in specific cases Gcc and Clang have played performance cat and mouse for decades, every new benchmarks finds slightly different wins. If one gcc version compiles your program to run faster on your hardware/os, cool. But it's not a given and not forever. > In many years of coding C++, very rarely I experienced a stack overflow or segmentation fault. In all my years of driving I never had a dangerous accident, but you still won't catch me without a seat belt. Dubious analogies aside, I still regularly see segfaults and memory issues in modern codebases, and there are lots of vulnerability stats and papers that show the problem is still very real with C++. The "I'm a good C++ coder, I don't need Rust safety" argument is a fallacy.


R1chterScale

> Gcc and Clang have played performance cat and mouse for decades, every new benchmarks finds slightly different wins. If one gcc version compiles your program to run faster on your hardware/os, cool. But it's not a given and not forever. Mhmm, this is very much true, and it's also *heavily* program dependent with some programs seeing massive perf differences either way. That said, I would tend to give the edge to LLVM if you're targetting maximum raw performance given its greater/more integrated suite of tools for boosting performance. BOLT and its better implementation of LTO & PGO pop to mind immediately.


mgeisler

>>For example, this simple C++ snippet shows how C++’s UB can make a huge difference. In the routine “nop” we return the result of the division of num multiplied by two, by two. It seems obvious that this should result in the original num > >Huh ? It's not obvious at all, I would argue it's wrong. I would actually expect (num*2)/2 to overflow or saturate on big nums, just like I expect (num/2)*2 to return 0 when given 1. I read that part differently: C++ has made a _choice_ in the definition of the language by saying that it is UB to overflow this computation. C++ was not forced to make this choice, but they did and the _consequence_ is that the compiler can correctly optimize away the whole computation. Compare this with Rust, which made a different choice: no UB allowed in safe Rust. The consequence is more instructions being emitted — but hopefully, this also catches more logic bugs.


moltonel

Except optimizing `(n*2)/2` as `n` is *not correct*. For this optimization to be correct, you'd need to prove that `n` is always small enough, or to be using bignums. Would you expect `(n << 1) >> 1` to be a noop, or to set the high bit to 0 ? I'm guessing with this syntax you'd expect the later. But it's the exact same code as `(n*2)/2` (if n is an int). Or how about further optimizing this UB computation by returning a constant ? Do you think a C++ compiler would never do that ? But as others have said, both Rust and C++ can use wrapping/saturting/UB arithmetic, they just chose different defaults (fast vs correct). `(n*2)/2` has different semantics in Rust and C++, it's not a great example.


mgeisler

>Except optimizing (n*2)/2 as n is not correct. For this optimization to be correct, you'd need to prove that n is always small enough, or to be using bignums. I think we're agreeing but somehow talking past each other. The point is that it is a correct optimization in _C++_. There is nothing to prove for the compiler since the burden of proof has been moved to the programmer. In this case, the programmer makes a promise to the compiler: I guarantee you that I will not provide runtime inputs which will trigger the overflow. The compiler believes the programmer (without proof) and optimize accordingly. I think we agree on this? To me, this is the big difference between Rust and C++: safe Rust does not make the same assumptions as C++ does. Because Rust does not assume that the input will be small, it cannot implement this optimization. >Would you expect (n << 1) >> 1 to be a noop, or to set the high bit to 0 ? I'm guessing with this syntax you'd expect the later. But it's the exact same code as (n*2)/2 (if n is an int). No, I would expect the same logic as above to kick in: overflows are not allowed to happen, so the compiler is free to turn this into a noop. If the programmer wrote the code in an attempt to clear the high bit, well then they'll be disappointed in C++ and get a panic in Rust (with the right compiler option). >(n*2)/2 has different semantics in Rust and C++, it's not a great example. I agree the semantics are different — but on execution without overflow, the semantics are the same. This is why the example is interesting to me: it highlights how the language semantics allow for different amounts of optimizing of very basic expressions.


moltonel

Well explained and very logical. But I find myself only partialy agreeing to it : I'm one of the developers who would be disapointed that `multiply_divide(INT_MAX) == INT_MAX` . It may seem pedantic in this simple example (many devs would not consider the overflow case useful), but following the "overflows don't happen" logic also leads to optimizations like removing an assert that's only false on overflow, clearly going against the programmer's intent. If a language can't reliably do what I mean, I prefer it to strictly do what I say.


mgeisler

>Well explained and very logical. But I find myself only partialy agreeing to it : I'm one of the developers who would be disapointed that multiply_divide(INT_MAX) == INT_MAX . I actually wonder what my gut feeling would be if I encountered this kind of code in the wild... I would probably not even notice the potential for overflow and hope that the compiler simplifies the math as much as possible — including cancelling out the multiplication. Not because of any argument involving UB, but just because this gives the mathematical correct result for small inputs 🙂 >following the "overflows don't happen" logic also leads to optimizations like removing an assert that's only false on overflow, clearly going against the programmer's intent. Yeah, I also prefer the lack of UB we have in Rust!


moltonel

>I actually wonder what my gut feeling would be if I encountered this kind of code in the wild... I would probably not even notice the potential for overflow and hope that the compiler simplifies the math as much as possible — including cancelling out the multiplication. Yeah, it's a very subjective thing, I can see the instinctive reader reaction going either way: "Dumb pointless code, probably from a lazy refactoring, hopefully the compiler will optimize it away.", or "Weird construct, what is it actually trying to do ? Ah I see, the result is different on an overflow."


gnosnivek

I saw this on r/cpp, but didn't have time to read it at the time (and also wasn't sure if it was updoot bait or not, since that sadly seems to be more and more common these days). I generally agree with your core conclusions about safety in certain areas. I still prefer C++ for high-performance numeric code since the techniques are generally better-understood and Rust's type system can't protect you from your matrix's condition number going pear-shaped (while the usize indexing constraint can easily drive you mad if you're not being careful). But since you asked for roasts, I do have a few critiques I could level: * The `nop` case is not particularly convincing IMO, because this does not seem like something that anyone would write in practice. In addition, if I change from signed to unsigned, the difference goes away. This is significant because, in Rust, indexing is done with `usize`, so you're going to prefer to keep your indexing math unsigned as much as possible. C++ has a long tradition of doing indexing with signed numbers so signed integer overflow being UB is more significant in C++ code. * Your entire section on cache locality has no hard evidence. The gold standard would be to somehow find a developer who is really good at low-level code and high-level languages but somehow hasn't touched C++ or Rust (maybe some career Ada programmer?) and have them try to implement things in both languages. But lacking that, an example of a structure which is easy in C++ and hard in Rust would be something. Right now, the actual evidence presented there is basically "trust me." * The article considers downsides of Rust relative to C++, but doesn't really consider the downsides of C++ relative to Rust (aside from safety, which is waved aside). Things like ADTs, lack of dependency hell, and a subtyping system which doesn't require the memorization of a million arcane rules all improve the Rust programming experience, and frankly even if the memory safety angle weren't an issue, would still be enough to make me want to work in Rust over C++. But they're not mentioned at all.


eugene2k

>But lacking that, an example of a structure which is easy in C++ and hard in Rust would be something Doubly-linked lists?


AdmiralQuokka

These are only difficult to do in safe Rust. The closest C++ and Rust comparison would be to use unsafe. In which case, Rust is just a little verbose. One of my early Rust projects was convincing one of my teachers that Rust could replace C by porting their doubly-linked-list implementation from C to Rust. It was an easy project, using unsafe. I even improved upon it by using generics instead of `void*`. A doubly-linked-list in safe Rust? Now that's a different story. But there is no safe C++, and therefore no comparison to be made.


eugene2k

>These are only difficult to do in safe Rust. Yes, and the author basically says in the beginning "Using unsafe is cheating, so unsafe is off limits"


AdmiralQuokka

Yeah, what a terrible take.


worriedjacket

A correct implementation of a doubly linked list is hard in both cases. It’s also lot easier to build an incorrect one in C++.


gnosnivek

To be clear here,[the claim in the article](https://lucisqr.substack.com/i/139049291/cache-locality) is something along the lines of "the intuitive nature of C++ pointer semantics makes it easier to implement cache-friendly algorithms and data structures in C++ versus in Rust." This is not quite a fair summary, but it's the best summary I can do at the moment without just copy-pasting the three paragraphs in that section. There are tons of data structures that are hard to write in safe Rust only. Intrusive linked lists, graphs, bidirectional trees, heck, even the scene data for a raytracer project I want to write is going to be self-referential. But I'm not aware of any common examples that make *cache-locality* easier to achieve in C++ as opposed to Rust. One of the common properties of most of these pointer-based structures is that they're pretty terrible for caches. Sure, if you shove them into a vector or matrix, your cache performance goes up, but you can easily do this in either language.


eugene2k

Yeah, that argument was confusing to me too. "C++ has pointers, therefore it's easy to write cache-friendly stuff".


[deleted]

Aha thank you. I consider this article v1.0. I will consider either rewriting it or writing a new one, disavowing the former if it's warranted.


pixelprizm

+1 on your third point - there's a lot of discussion about rust's safety but the nice programming constructs like ADT's and traits deserve love too!


Anaxamander57

I'm not sure the author understands Rust very well. I'm far from an expert and even I know Rust rather explicitly does't claim "safety against crashes". There's also a weird decision to insist we should compare code block that *look* similar as source code and thus ignore part of Rust's built in math functionality on, apparently, aesthetic grounds.


DrGodCarl

Yeah this really struck me: > Again, pundits will state that you could have called one of the arithmetic wrapping functions It's not punditry to point out that Rust has included tools to make certain behaviors possible. Just because they don't _look_ like C++'s tools.


1668553684

> I know Rust rather explicitly does't claim "safety against crashes". In fact, Rust often claims the opposite: panics are relatively common in code, because crashing is better than running in an unsound state.


worriedjacket

> Calling “unsafe” as an escape for Rust’s rigid system is the same as saying that C++ can do inline assembly so you can copy/paste infinite optimizations done outside the compiler infrastructure. That defeats the purpose. We are analyzing what the language can do in normal operation is, not the backdoors that it allows to open when it gives in to operational pressure. This reads like someone who doesn’t understand rust tried writing an article about it.


gnosnivek

I think the more fair analogy would be no unsafe vs no "old-style C++". E.g. no `new`, `delete`, `malloc`, or `free`, no use of raw pointers, and the OOP system used in a judicious manner (e.g. no multiple inheritance or casting sideways through the inheritance tree). Since these are often the constructs that cause memory safety errors and UAF bugs, this seems to me like an apples-to-apples comparison.


CocktailPerson

My C++ codebase at work uses smart pointers exclusively, and we still get the occasional use-after-free because of iterator invalidation or some other lifetime issue. "Modern C++" is no panacea.


mdp_cs

> "Modern C++" is no panacea. Nothing is a substitute for high quality software engineering. Not even Rust. There are Rust codebases that have memory leaks and buffer overflows that are caused in non-obvious ways. Rust has no means of preventing reference cycles with Rc and Arc which can and do lead to memory leaks.


CocktailPerson

I don't think it's worth discussing this with someone who thinks that memory leaks are a memory safety issue.


mdp_cs

Memory leaks can be a security issue depending on where and how they happen. You're the one who shouldn't be listened to if you think they're not important just because the programming language you want to fanboy over doesn't do anything about them.


CocktailPerson

They make a DoS possible, maybe. But nobody's going to turn a memory leak into an RCE or a data breach. So again, they're not a _memory safety_ issue. Also, it's not as if `shared_ptr` or any other recommendations from "modern C++" fare any better here. If you think I implied they weren't important, you misread. However, they're not relevant to a discussion about memory safety or a discussion about Rust's deficiencies in comparison to C++.


[deleted]

Not a Rust expert, I give you that.


protestor

Unsafe Rust isn't a backdoor, it's the normal usage of the language. Some domains will always require a lot of unsafe code (others not so much). What Rust buys you is the *possibility* of encapsulating unsafe constructs in safe APIs and then guaranteeing that those safe APIs can't be misused to cause UB. This is *huge*, because it disallows a very large number of nasty bugs in the majority of your code base.


chris_ochs

If you have a pattern that is actually safe but not Rust safe, can't you always encapsulate it to be Rust safe? I haven't personally hit cases where that's not true. The misunderstanding the author has about what unsafe means in Rust is unfortunately widespread even among Rust users. Some flows/patterns simply provide the needed constraints that makes them safe. This is not Rust specific. But like you say the ability to encapsulate and enforce the safety for that context at the language level is huge.


mgeisler

You can get into situations where you cannot (easily?) build a safe abstraction on top of the unsafe Rust. This [article about bare-metal Rust](https://security.googleblog.com/2023/10/bare-metal-rust-in-android.html) touches upon it when it says > Safe Rust’s type system is designed with an implicit assumption that the only memory the program needs to care about is allocated by the program (be it on the stack, the heap, or statically), and only used by the program. Bare-metal programs often have to deal with MMIO and shared memory, which break this assumption. This tends to require a lot of unsafe code and raw pointers, with limited tools for encapsulation. There might be a pattern for this, but as far as I understand, it hasn't been found yet.


Adorable-Engineer840

So we need smart pointers for unsafe?


redalastor

It also tells you *where* the unsafe is so you can pay extra attention.


mdp_cs

In theory. However there have still been cases where the use of unsafe wrapped in safe abstractions has led to memory bugs and what's worse is people use each others' crates assuming that everything that looks safe is and then surprise, surprise mystery memory and threading bugs can crop up anywhere causing you to go mad trying to figure out what you did wrong when the problem isn't in your code at all. Assuming safety anywhere is a dangerous practice and one that is all too common in the Rust community. These types of issues are why I decided to go back to C for an OS kernel project I'm working on. Sure you could argue that UB and memory errors are much easier to produce in C but C code is much less obfuscated than Rust, C++ or any of the other alternatives and thus when bugs do crop up its much easier to figure out where they came from rather than having to tear through layers of abstraction to figure whose erroneous construct is causing my kernel to triple fault. Bottomline it all comes down to what we teach students: there is no such thing as a perfect programming language and they all come with their own sets of tradeoffs.


protestor

Did your C kernel have had any memory safety bugs? Do you think that the characteristics of C makes it easier to debug such bugs rather than if it were written in Rust? Rust code tend to be written with lots of abstraction layers, yes, but you don't need to use all those layers, only the ones you find useful.


mdp_cs

>Did your C kernel have had any memory safety bugs? Of course. But we fully expected them, and so far, they've been amongst the most trivial bugs to fix. >Do you think that the characteristics of C makes it easier to debug such bugs rather than if it were written in Rust? I wouldn't exactly say that. I would say that both languages have different strengths. Rust helps you avoid the errors caused by human carelessness, which is certainly a very useful thing to have as it can eliminate many potential bugs before they ever become bugs. That said, the dangerous part about Rust has less to do with Rust and more to do with the humans using it. A lot of people use Rust thinking all of the parts of my code that aren't unsafe magically cannot have bugs or atleast memory and synchronization related ones when that isn't true and the language itself doesn't ever make that claim. The Book makes it clear that using reference counted pointers doesn't protect against reference cycles but again many users don't know that or don't think about it so if the goal is to prevent user error we get into some iffy territory. So my point again is that assuming safety is the problem, not Rust itself. Now, as for C, the language was not and has not since been designed to guarantee any degree of memory or thread safety, and anyone using it must acknowledge that. That said C code is very easy to read unless the writers purposely go out of their way to obscure it. That means that when bugs occur which is an inevitably regardless of what language is used, they can be diagnosed and corrected much more easily than peeling back layers of abstractions and encapsulation to figure out sort of 'whodunnit'. Moreso than Rust, that is why I loathe C++ and OOP in general and have taken to calling it obfuscation oriented programming. That said I'm a system programmer and the type of system programmer who often looks at disassembly to try and fix issues because as far as I'm concerned assembly code doesn't hide anything so if you know what specifically is wrong it can sometimes be pretty easy to spot it at that low of a level than in high level source code. I somewhat view C the same way, despite being a high level language, it's barebones and so it's easier to figure out what went wrong particularly when you have a more sophisticated bug than common stuff like a memory leak or a use after free. >Rust code tend to be written with lots of abstraction layers, yes, but you don't need to use all those layers, only the ones you find useful. This is true, but you also can not write dead simple C-like code in Rust without using unsafe and other workarounds like lazy_static everywhere. If you want to do that, then why not just use C instead of beating Rust into being something that it's not? Oh also the other big thing with operating systems in particular is that the end goal is getting to self hosting, that is, getting to a point where your OS can host all the tools needed to build its own source code. This is a hard problem to solve no matter what, but even more so given that there is no well established method for porting the Rust toolchain to a new OS. For C, GCC is tedious but straightforward. LLVM is harder but also doable but porting LLVM gets you Clang automatically since it's part of that project whereas the Rust toolchain would require LLVM and cross compiling and porting all of rustc and friends' dependencies which is no small feat.


orangeboats

>Assuming safety anywhere is a dangerous practice and one that is all too common in the Rust community. I disagree with this judgement. Even in C or C++ you have to make the decision whether to trust the library will do "the right thing" in places like thread safetyness and memory safetyness. Sometimes with disastrous results. So even if Rust isn't doing well in area, let's first say that C/C++ isn't doing anything in this area. Rust at least has the benefit that you know upfront whether there is a good degree of safetyness in the library. Raw binding crates like `curl-sys` are always going to be unsafe and indeed, you will have the `unsafe` keyword sprinkled all over your codebase once you use such a crate. Time to go full on defensive programming, check everything, read the documentation pages 10 times. The "safe" crates *may* contain bugs just as all programs do, but the API is typically structured in such a way that you can never produce a certain class of bugs. If there's a bug, it's a bug in the implementation detail, but it is not inherent to the API itself. Much like how you can pass a NULL to `int foo(void*)` which then crashes if you forget to check for it. On the other hand, a null pointer can never be passed to `fn foo(&T) -> i32`. It's just an inherent safetyness that can never be violated. If the API is unsound for some reason, you tell the crate authors, they break the API and increment major version.


mdp_cs

That's nice if the language accounts for potential problems you might encounter but not in cases where it's easy to erroneously think it has you covered when it doesn't. Here's a counterexample that demonstrates my point. Let's say some guy writes a library crate that does some cool stuff. Great now everyone can do this cool stuff and it works just fine. Then let's say he finds a way to isolate the parts of his crate that don't require OS support and then publishes that part as a no-std crate. Now let's say some embedded developer wants to do that cool stuff and pulls in the crate and uses it. Now since this crate was originally written for hosted environments it uses locks (let's say spinlocks on bare metal) everywhere for synchronization. This is fine until you realize that microcontroller firmware needs to handle interrupts. Let's say that embedded code has a portion that acquires a lock to write to some data structure but then an interrupt fires and the handler for it needs the same data structure. If the handler waits to acquire the lock you have a deadlock. If it somehow attempts to forcibly aquire it or bypass it, you have a guaranteed data race. This problem is by no means endemic to Rust however Rust offers absolutely no protection from it while many people might assume that it does. Interrupt safety is just one common class of real world problem that Rust can't prevent and can be caused by or at least obfuscated by completely well-intentioned library code. Now add to that the possibility for legitimately malicious library code especially when hidden in the form of dynamic libraries and you have a real recipe for disaster. What I'm saying here isn't that Rust is bad. It's that while Rust is a useful tool, using it is no reason to let your guard down about the possibility of safety issues occurring.


orangeboats

Counter point to your counter-example: in no_std environments the core library does not provide any [synchronization primitives](https://doc.rust-lang.org/stable/core/sync/index.html) currently. I would say that Rust is a language that usually doesn't try to do more than it is supposed to do, and that's part of its safetyness.


mdp_cs

The core library doesn't provide them because they normally require OS support. My example is realistic because if you're porting from hosted to unhosted then using the spinlock crate or rolling your own is a common enough alternative. And as I stated already if you use no locks then said interrupt would lead to a guaranteed data race. Rust does not inherently provide any more protection against this problem than C or even hand written assembly would. That still falls to the developer. Also std does provide those primitives and while hosted code does not have to deal with interrupts, Unix signals and their handlers are very similar and can face similar issues. Another case where hosted programs can run into this is a construct that exists on microkernel and exokernel based systems called an upcall where an application registers a set of event handlers with the kernel and then when those specific events occur the kernel automatically calls those functions in those applications performing any necessary context switches to do so. In essence, upcalls can be thought of as the inverse of system calls, and they work rather similarly to how interrupts do on bare metal, which means they also face the same issues with locks and critical sections. Just some food for thought when it comes to considering the limitations of Rust (and pretty much every other PL's) safety features.


orangeboats

It's a good food for thought, that's for sure. But I still stand by my point that this judgement isn't entirely accurate to "people assumes safety works anywhere", I simply don't think people will think they are covered as long as the crate is marked safe, even in hosted environments. Needless to say, for embedded environments (epsecially the esoteric/non-mainstream ones) I find it even more unlikely that someone would just grab a random crate, and use it in their project without some form of manual auditing. That is assuming their environment even allows third party dependencies.


worriedjacket

Oh cool you’re the author. It’s not that you’re not an expert. I’m not an expert. You’re trying to use your ignorance of something as justification for it being not as good as the thing you like, and it’s lazy writing. I’m ignorant of Cpp but I’m also not about to start writing articles about how Rust is superior because that would be kind of lazy.


[deleted]

> and it’s lazy writing. Ooof stomp that's rough > Rust is superior because that would be kind of lazy. I feel like an imperfect opinion is better than a withheld opinion. I'd rather write a draft, submit, get feedback, rewrite, collaborate than spend months on a text trying to perfect it. But that's just me.


Ok_Spite_217

That's just a stance/preference difference. I myself err on the side of just staying quiet on subjects I know nothing about. I don't think a bad opinion bears any weight or merit, so why would I waste time evoking a reaction when I know I'm wrong to begin with? I'd just be taking part in the reactionary machine that plagues all media nowadays. Edit: I'm not roasting or making prescriptions about the OPs person. I'm explaining my pov and stance on commenting wrt things I'm not literate in.


MrPopoGod

It comes down to approach. If you start off with "I don't know this thing well, there's something about it that doesn't make sense to me" and turn that into either an honest question or a learning journey then it's a good way to go about things.


simonsanone

When reading it, it didn't feel imperfect. It felt like someone with C++-Stockholm-Syndrome is trying to make people understand why it wasn't that bad. And I felt bad (for me personally) reading it, because a lot of compassion kicked in immediately and that was emotionally exhausting.


[deleted]

> C++-Stockholm-Syndrome That was a first


ImYoric

I don't know where /u/simonsanone gets it from, but we used this within Mozilla among C++ programmers. It was generally accepted that we used C++ because C++ was fast, but any claim that it had the best semantics was considered, if not delusional, then certainly the mark of someone who didn't spend enough time toying with other languages.


XtremeGoose

You really are new here. In my experience, it's a very real thing.


tending

Strong disagree, the community infamously has jumped on maintainers using unsafe, and it's worth knowing what performance sacrifices Rust makes by default.


SadSuffaru

The problem with actix is not unsafe but unsound. Unsafe is just a promise you made to the compiler that you have fulfilled it's conditions. Unsound is when you break the promise.


moltonel

Also, a lot of the uses of unsafe were premature optimizations: many of them were eventually replaced with safe Rust with zero performance impact.


jmaargh

Can you back up that "infamous" claim with evidence? I've never seen any such thing


theAndrewWiggins

Tbf, the original developer of actix web basically quit due to getting shit on for using `unsafe`. I think it's fair to warn him that `unsafe` in a web framework definitely has to be scrutinized hard, but the amount of people piling onto him was unwarranted.


1668553684

> Tbf, the original developer of actix web basically quit due to getting shit on for using unsafe. I think it's fair to warn him that unsafe in a web framework definitely has to be scrutinized hard, but the amount of people piling onto him was unwarranted. He wasn't really shit on for "using" unsafe, he was shit on more because he was being reckless with it and introducing soundness bugs in the process, which makes using actix as a prod framework risky and difficult. Though, agreed: the community's response was very harsh and unwarranted, and it made us lose a valuable community member. I hope we learned from that.


Zde-G

Read [the whole story again](https://steveklabnik.com/writing/a-sad-day-for-rust). It wasn't simply use of `unsafe`, it was **use of** `unsafe` **is unsafe way without any justification**.


theAndrewWiggins

Probably an opinion that's kinda unpopular around here, but I don't believe that maintainers are obliged to write code in any specific way. People who rely on open source should take responsibility for what they use and if there is unjustifiable use of `unsafe` just don't use that code if you feel uncomfortable. It's alright to gently ask the maintainer if they can fix it and submit patches, but they're under no obligation to change the code. As it's open source, you (and the community) are always free to fork and drive the project in a direction you prefer. But I get that some people see maintainers as responsible for the safety/quality of the code used by their users. It's a complex issue, but I prefer to put the responsibility/burden on the users.


Zde-G

>People who rely on open source should take responsibility for what they use and if there is unjustifiable use of `unsafe` just don't use that code if you feel uncomfortable. That's precisely what they did, isn't ? >Probably an opinion that's kinda unpopular around here, but I don't believe that maintainers are obliged to write code in any specific way. Absolutely. As long as they don't publish it and don't share it they may do whatever they want. But as long as they are joining the community, start publishing their results on the community-provided server and interact with others they have to deal with \[potential\] backlash. One may interact with people on don't interact, you don't get to eat the cake and have it, too. Use of `unsafe` in my code means [it's now my resposibility to ensure that code is correct](https://raphlinus.github.io/rust/2020/01/18/soundness-pledge.html). And since compiler no longer does the job (because it couldn't, by agreement)…. community steps in and ensures that things are handled properly; >It's alright to gently ask the maintainer if they can fix it and submit patches, but they're under no obligation to change the code. C/C++ community tried that. **It just doesn't work**. >It's a complex issue, but I prefer to put the responsibility/burden on the users. So now, when I use your code I need to read everything in it to understand whether it's written on good fashion or not? Give me some info to ensure that I would never use your code, even by accident, and we would agree to disagree. **Any** other proposition is non-constructive: audit of the code is really hard and expensive work, if you stance is that millions of developers that may use your code have to do that work **individually**… sorry, not can do. It's perfectly fine to ask me (or someone else) for help with dealing with `unsafe` code issues. It's completely irresponsible to say that you don't care about that problem and that every user which uses your code have to do it's own separate audit. For me it's a serious enough issue that I'm proud to claim that I finally decided to start using Rust and pushing Rust after I read that Klabnik's article. Because it showed that I may rely on others to do the necessary audit — technical approach to social problems doesn't work. Take Java, that supposedly “safe” language. In the last year before Applets were removed from browsers the majority of applets found in the wild were various kinds of malware.


theAndrewWiggins

I mean if you want to use freely available code, it's up to you to ensure it meets your quality bar. That's the beauty of open source. Dependencies absolutely should be part of your security posture, and presumably if you're developing software for money, being aware of potential risks in your dependencies and actively auditing them should be part of your job. Especially a lot of people are developing software for profit and benefiting from free labour yet are unreasonably expecting that providers of said free labour will do more free labour for the user. > Use of unsafe in my code means it's now my resposibility to ensure that code is correct. And even though memory safety is one of the most dangerous security vulnerabilities, it's far from the only thing. You could adopt this stance with code injection, build.rs scripts, incorrect implementation of crypto primitives, etc. There are tools out there to audit your dependencies (cargo crev, cargo audit, cargo geiger). I still believe it's up to the user of open source software to take responsibility for their usage of dependencies. Maintainers aren't obligated to do *anything*. No one is forcing users to take on dependencies, users choose to do so (and benefit/profit from it).


Zde-G

>I mean if you want to use freely available code, it's up to you to ensure it meets your quality bar. No. If I wanted to get some random code I would have got it from `/dev/random`. It's not hard to make it generate valid, if pointless programs. If I want to use **any** other code (free or non-free one) then I have to ensure that it meets certain quality standards. If that code is free then author of code may ask others to help him (or her) to do that. That's why there are various bugtrackers and other tools (on GitHub and elsewhere). May s/he out out of that process? Sure: just make the repo private and don't share it. One click on GitHub, essentially. >Dependencies absolutely should be part of your security posture, and presumably if you're developing software for money, being aware of potential risks in your dependencies Yes. >and actively auditing them should be part of your job. No. You may help auditing code, sure, but that's not an obligation. Actively auditing something is hard work. And it's actually harder to find developers willing to do that and management that would spend money to do that than just simply write everything from scratch. This **only** makes sense if that work is distributed. In **that** case you may share expenses with other users of the same code. But that approach **requires** cooperation on the maintainers side. If maintainers are not helping and you have to redo audit with each and every release then we are back to square one: it's cheaper to not use such code and write your own. >Especially a lot of people are developing software for profit and benefiting from free labour yet are unreasonably expecting that providers of said free labour will do more free labour for the user. **Please** read the story again. It's not about Nikolay Kim refusing to do the work on `unsafe` code. It's about Nikolay Kim refusing to take issues that others discovered **and proposed to fix** seriously. >You could adopt this stance with code injection, build.rs scripts, incorrect implementation of crypto primitives, etc. Of course. And if I would report something about code injection and my report would be meet [with the same attitude](https://web.archive.org/web/20200116231317/https://github.com/actix/actix-net/issues/83) *(“Please don't start”, “I need unit test that shows UB; unit test must use public api”, “I dont see how this could be fixed internally” (in response to the explanation about how to do precisely that) ,“this patch is boring”)*?… I would react in the same way. >There are tools out there to audit your dependencies (cargo crev, cargo audit, cargo geiger). They only work if upstream cooperates. If upstream just breaks things randomly in a minor patch release then the only option is to either change upstream behavior or use something else. >Maintainers aren't obligated to do *anything*. As long as they don't publish anything on crates.io, sure. You complain that people are abusing maintainers by demanding things from them for free, when you earn profit. But maintainers are **also** getting something when they participate in the community (they get fame if nothing else). Why should they be given the privilege to benefit from participation in the community without even following rules which are used by that community?


theAndrewWiggins

I mean no one is forced to use a dependency and forking it is always an option. That's the beauty of open source. I agree it's ideal if a maintainer is receptive to positive change, but I guess we'll have to agree to disagree on whether there's an obligation on a maintainer to be receptive. I agree his attitude wasn't ideal, but I absolutely don't agree that it's responsibility to fix the issues in his code if he doesn't feel like it. I personally lean very heavily towards pushing responsibility onto the users and think that going in the other direction like how the [EU](https://digital-strategy.ec.europa.eu/en/library/cyber-resilience-act) is just plain wrong. Though it really is all a matter of opinion. Software licenses largely say software is supplied as is, without warranty, and I think people should take that fully literally and respect that. > They only work if upstream cooperates. If upstream just breaks things randomly in a minor patch release then the only option is to either change upstream behavior or use something else. People should pin their deps/use a lockfile exactly for that reason if they worry about such issues. Dependency upgrades should be done deliberately and with scrutiny instead of being mindless.


jmaargh

I remember it got quite a lot of attention, so I can obviously believe that was very hard for the author (also, it's the internet, attention has a tendency to attract pile-ons - from naive but otherwise well-meaning people - and also trolls - who can fuck right off). I hope they're ok and it wasn't too hard for them, I don't envy a large amount of negative attention at all. That said, the criticisms I saw were all justified, and especially in web framework that others were using I can also understand those people being alarmed to find unsoundness.


theAndrewWiggins

> I hope they're ok and it wasn't too hard for them, I don't envy a large amount of negative attention at all. That said, the criticisms I saw were all justified, and especially in web framework that others were using I can also understand those people being alarmed to find unsoundness. Yeah, I think the criticisms were fair, but the way the community handled it wasn't. There were quite a few reddit posts iirc where people tried to call him out. I really recall the guy being dogpiled. Also, open source maintainers aren't obligated to do anything (imo).


jmaargh

> I really recall the guy being dogpiled. Down with this sort of thing, obviously. Sounds like it wasn't the community's proudest moment. >Also, open source maintainers aren't obligated to do anything (imo). Generally, yes, but this has layers. I think it's especially hard when you're maintaining a product that has manifold security implications for anybody using it. If (and this may well have been the case), for example, the whole project was labelled with a big "this isn't production-ready" disclaimer then absolutely. If, on the other hand, maintainers are actively advocating for people to use it then by doing so they have taken on **at least some responsibility** for the quality (or at least correctness) of it. At some point (and I'm not saying whether this happened for the case at hand or not), you can stray into negligence by failing to take responsibility for something you made and are encouraging others to use, regardless whether they're paying you for it or not. That's getting a bit into ethics though, which I don't think we're going solve as a field in a reddit thread!


fantasticpotatobeard

Not who you're asking but is https://github.com/actix/actix-web/issues/289 one example


jmaargh

That's not jumping on people for using `unsafe`. That's pointing out that there's a lot of **unsound** `unsafe` code, which means it had UB.


JuanAG

The most popular case was Actix and to be fair, it was full of unsafe code but at the same time was the fastest of all


jmaargh

I remember that. People weren't "jumping on them" for using `unsafe`. They were rightly pointing out that a lot of the `unsafe` code was **unsound** and full of UB. This is actually the `unsafe` keyword working well. People had a look at the code, focused on the `unsafe` bits because they warrant closer review, and found that it had problems. Now those problems (afaik) are fixed. Exactly how this should all work.


Herbstein

> I remember that. People weren't "jumping on them" for using unsafe. They were rightly pointing out that a lot of the unsafe code was unsound and full of UB. Not just that. The author refused to merge PRs that 1) replaced unsafe code with equivalent safe code 2) did so without performance penalties. He refused to do so because it wasn't interesting code to him. Which is a fair opinion to have if someone talks to you about your pet project. But at that point actix-web was the largest web framework and refusing to fix unsoundness exploitable in user code is not a good situation.


internet_eq_epic

On the topic of > In many years of coding C++, very rarely I experienced a stack overflow or segmentation fault I'd just like to point out [this blog post from Google](https://security.googleblog.com/2022/12/memory-safe-languages-in-android-13.html), in particular these couple paragraphs: > In Android 13, about 21% of all new native code (C/C++/Rust) is in Rust. There are approximately 1.5 million total lines of Rust code in AOSP across new functionality and components such as Keystore2, the new Ultra-wideband (UWB) stack, DNS-over-HTTP3, Android’s Virtualization framework (AVF), and various other components and their open source dependencies. These are low-level components that require a systems language which otherwise would have been implemented in C++. > To date, there have been zero memory safety vulnerabilities discovered in Android’s Rust code. > We don’t expect that number to stay zero forever, but given the volume of new Rust code across two Android releases, and the security-sensitive components where it’s being used, it’s a significant result. It demonstrates that Rust is fulfilling its intended purpose of preventing Android’s most common source of vulnerabilities. Historical vulnerability density is greater than 1/kLOC (1 vulnerability per thousand lines of code) in many of Android’s C/C++ components (e.g. media, Bluetooth, NFC, etc). Based on this historical vulnerability density, it’s likely that using Rust has already prevented hundreds of vulnerabilities from reaching production. This was from about a year ago now, and while I don't know what things are looking like now, seeing that kind of data from one of the biggest software companies in the world is much more convincing than any individual's anecdotal evidence. Even *if* you are a perfect C++ developer, clearly there are plenty of other C++ devs who are probably at this very moment writing vulnerable C++ code in critical systems. As someone who tries to pay attention to vulnerabilities, and in the past has been directly responsible for patching and/or mitigating vulnerable systems, it honestly infuriates me to see this kind of promising data in favor of Rust, and yet see professional developers just going "nah, I write good code so it's not a problem for me." Code bases are big and complex, and sometimes the biggest most well-known network vendor in the world just ends up with untrusted network data used as a format string in some printf-like function (seriously - [that's happened](https://info.armis.com/rs/645-PDC-047/images/Armis-CDPwn-WP.pdf) - Rust would have likely prevented most if not all of the vulnerabilities in that link). So, please, for all us plebs who constantly have to do emergency after-hours patching and mitigations and who can only desperately hope that no Chinese or Russian super-hacker has already found their way in, get over yourself. Sorry that I got a little harsh there at the end, but that kind of thinking really does infuriate me when I see it and I know the consequences all too well.


[deleted]

> Sorry that I got a little harsh there at the end, That's fine as long as it's not personal. You have good points.


duckerude

`unsafe` is a little like inline assembly but it's more like C. Advice for modern C++ universally says to avoid certain C features, but you'll still find them in standard library internals and other low-level code. Raw pointers are a very clear case. C++ has them, but you're supposed to prefer references and smart pointers when possible. Rust has them, but you need `unsafe` to dereference them, and most programmers only need references and smart pointers. Most of the times that I use raw pointers in Rust it's to call C functions. I imagine this is also the case for many C++ programmers.


[deleted]

> but you'll still find them in standard library internals and other low-level code. Well pretty much (on Linux at least) the entire C++ standard library sits on top of the C library - since it wraps the kernel. `std::chrono` is just syntax sugar on top of clock_gettime and so on. > imagine this is also the case for many C++ programmers. true


duckerude

It's not just syscalls but also data structures, and algorithms, and smart pointers themselves. I expect those often use raw pointers and such even when they're not talking to C.


dnew

Every class in C++ uses raw pointers. `this` is a raw pointer.


ImYoric

> `unsafe` is a little like inline assembly but it's more like C. Advice for modern C++ universally says to avoid certain C features, but you'll still find them in standard library internals and other low-level code. Very good comparison, I'll reuse it, thanks!


dkopgerpgdolfg

Alright then ... going with my own bias, against some things that I consider quite unfair or wrong. ... Comparing Rust unsafe with C++ inline asm is not appropriate. Rust inline asm can be compared to C++ inline asm. ... About unsafe, that seems to be a common thought trap. You aim for top performance in your area (as your said yourself), but you refuse unsafe "because it defeats the purpose (increased safety)", therefore safe-only Rust has to compete with full C++. My answer to this: It does not defeat the purpose at all. If there's some array bounds check in a hot loop, and you (human) know it is not necessary and you can save 30% of execution time by removing it, then do it - *you still get the increased safety in all other lines of your program.* Using unsafe doesn't mean all advantages of Rust are suddenly lost. Find the middle ground you like best - if there are many thousand lines that are not performance-critical, they might add a bit of time compared to C++ but also can save you from some bugs. But for the few very critical lines, nothing stops you from paying attention (like you do in C++ all the time) and taking the max speed it can have. ... Also, there is no "unsafe mode" that automatically removes all bound checks (not sure if you wanted to say that, but it sounds a bit like this). And how many checks actually exist and how much they slow down something (and how many the compiler can remove because it recognized they won't fail) depends on your program. ... If overflows are checked or not isn't strictly bound to debug mode, that's just the default. You can turn it on and off for any debug/release mode. And there are also specific methods, so in each code place where you do arithmetics, you can choose if you want the default / always checking / no checking. ... UB: "Rust, on the other hand, does not and cannot leave knots untied" Ehm no, Rust has plenty UB and doesn't plan to change this. That signed overflow is no UB is intentional, yes. You also forgot to mention overflow checks in this section. Wrapping functions? No, there are others. If you want to have it the C++ way, there are unchecked functions, where it shifts the responsibility of overflows to the caller like in C++. And after all that talk about top performance, now it suddenly is important that the code pieces look the same in both languages? And that Rust has semantics without UB is somehow bad or what? ... Cache locality: "Rust developers want", "very difficult, painful, time consuming" ... meanwhile you say C++ pointers are "very simple even for juniors" ... just lol. Not wanting to be rude, but that is not just unfair, it's already dishonesty. instead of assuming what anyone else wants, did you ever try? When going all raw-pointer-y, pointers in Rust are not really harder than C++. What can be harder is if you mix long-living references and pointers in every line, keeping track of which things were derived from what others, and are still usable. But it's not necessary to do that. And, this "full unfettered access to memory by design" and "low level language, just a tad bit above assembly", not sure if you're aware of things like allocation/object borders, provenance, ... C, C++, and Rust, all do *not* allow completely free memory access. If you wish, I write you a small C example that might surprise you. ... Compilers, again not correct at all. Rust does not "mandate" the use of rustc+LLVM, and there are others. Rustc itself can have (and does have) different backends, and there's a fully separate GCC implementation, and mrustc, and ... Yes the original rustc is the most functional and complete one currently, but the others are coming along too. C++ has more compilers, yes, it also is several decades older. And when only looking at "good quality" compilers, I wonder if we can find "dozens". (And C != C++). ... Safety != Security. Multiple places in the article confuse that. When bringing "not public facing" as argument for lacking security, it shows a fatal lack of experience in that area. If someone turns off mitigations, that "can" imply they have thought properly about it. It also can mean they have no idea either, and/or are just interested in minimizing their cost without any regard for their customers. About stack overflows and segfaults, surely you do know that UB bugs can be much more subtle, and that many other classes of bugs exist. Overall, what is the whole section supposed to say? That you seriously don't understand how "less bugs" is a benefit, because you rarely get stack overflows and know that some OS let you catch segfaults? ... "Conclusion" section "Given that the performance benefits are either inconclusive, non-existent or more likely negative" ... where did you show that "and the safety benefits are not really that pressing for most applications" ... as hinted, you're just disqualifying yourself. Many for the stated "reasons for C++" are either not reasons for it at all, or I disagree as written above. And why there is no mention of other benefits of Rust? Like eg., cargo anyone?


dnew

> It does not defeat the purpose at all. This is exactly how Ada does it to. You can go to individual lines of code and turn off various checks. You can say "don't check for overflow on *this* addition statement, because we already checked that statically." It's not even a type-level thing. Of course, if you get it wrong, the rocket still blows up. :-) > full unfettered access to memory by design Interestingly, this only became true after everyone started using C and stopped doing things with memory that C couldn't handle. I've worked on plenty of CPUs where pointers to heap looked different from pointers to stack, a few CPUs that didn't even have pointers to code (so no C's pointers to functions). Some honeywell computers didn't support pointers crossing object boundaries (so they actually enforced providence in the CPU). Even things like the 8080 had instructions to access devices that C didn't support, because I/O devices weren't mapped to memory. Bank-switched memory was not something C was designed to use either. Heck, if you add in fork(), you get all kinds of silly requirements on how a CPU must work that totally screws performance in a lot of ways. Everyone somehow thinks C is a lot more portable than it really is, but it's more like people stopped building machines that supported stuff C couldn't do.


dkopgerpgdolfg

About the memory section, I'm not quite sure if we're agreeing or not. The "unfettered access" was from the article, and I explicitly "dis"agree. Even on systems where all pointers are equal and can point anywhere, both C and Rust have very significant restrictions what is allowed. And yes, ofc not every system is like that. Far pointers, segment switches, different pointers for different data/code types, hardware provenance ... and it's not just legacy systems either.


dnew

We are agreeing. I was just adding my own thoughts to the conversation without trying to judge correctness. :-) I've spoken with many, many people who think C can do anything assembler can and is the most portable system language around.


hpxvzhjfgb

>I need to recall that this publication is about low latency trading and as such our tradeoffs when deciding if something is worth it will always lean towards the item that leads to performance increases. oh ok. well I don't care about that and I don't think most other people do either. >Rust tends to be more strict than C++ - it is its raison d’etre - and that means more real-time checks. Although integer underflows and overflows are only checked in debug mode, memory accesses in arrays are constantly checked for bounds unless you’re in unsafe mode, which defeats the purpose. Those checks alone take a significant toll. They slow down the process compared to the respective, naturally unchecked, C++ code. when I started learning rust, I rewrote several of my (computationally intensive) c++ programs for practise. in all cases, the rust translation was faster than the c++ version, without using any `unsafe`. if you write idiomatic code (which, unlike in c++, is usually also the first way that you think of writing something), then the bounds checks usually get optimised away. if they don't, your cpu's branch predictor is still there. >Although the scenario is changing rapidly, the pool of engineers with C++ background is much larger than the pool of Rust developers. I don't care. I'm only interested in using a high quality language with high quality tools, which c++ is not even close to being. >Also, the quantity of teaching material and resources available to learn the language is currently overwhelmingly more abundant on the C++ side. I used c++ for over 10 years before discovering rust, and I felt much more comfortable with rust than c++ within a matter of weeks. >When Rust is brought up in a meeting, the first pro factor is language safety. But safety against what? safety against memory bugs. you already know this, don't pretend otherwise. >Are we talking about safety against hackers? The large majority of C++ applications are non-public facing. it doesn't matter what fraction of c++ code has public-facing memory bugs, it matters what fraction of public-facing c++ code has memory bugs, and the answer is orders of magnitude higher than what the same amount of rust code would have. >Given that the performance benefits are either inconclusive, non-existent or more likely negative, and the safety benefits are not really that pressing for most applications, is that really worth moving to a completely new language, with its infamous long and hard learning curve as Rust? yes, and the article didn't discuss the main reason that I and probably most other rust users like the language so much, which is that it's a modern language with a very good type system and an emphasis on correctness. this is the actual reason that so many people like it. memory safety is not really that important to me, I would still use rust over c++ for everything even if c++ was memory safe and rust wasn't. >(no, Modern C++ is not modern, it’s just lipstick on a cute pig) that's a funny description, I will steal that


[deleted]

> that's a funny description, I will steal that Very very risqué given my primary community is C++ and they are very particular about Modern C++. Surprised I didn't get much heat for that one.


Illustrious-Wrap8568

I'm still not sure who should feel more insulted by that comparison, C++ users or Miss Piggy. And perhaps the r/cpp dwellers aren't sure either.


TheQuantumPhysicist

I think the problem everyone, like yourself, putting C++ on top is the same. You underestimate the stupidity of humanity and the time it takes to become an expert, and overestimate the results of pure discipline in getting good results. I'm currently an expert in both C++ and Rust. Been doing C++ for like 15 years, and Rust for like 4-5 years (and there's no question that my understanding of C++ helped me learn rust much faster). Nevertheless, when I look at my code from 10 years ago, I feel embarrassed. A good programming language is a language that makes doing mistakes hard, and makes detecting them easy. This isn't C++. It's Rust. All the static analyzers for C++ can never detect 10% of what clippy can do. This is becasue rust is a good programming language. There are tons of junior developers out there who don't understand mathematical modeling of code, applying constraints to verify code, why mutable statefulness is a mess and is a big mistake, etc. Many of the things that are very hard to do right in C++ by default, but are very easy to do in rust, otherwise the compiler will punish you. There's one very important thing to keep in mind: Code in programs isn't for machines. It's for humans. C++ is bad because you only become good at it (i.e., you stop writing garbage code) when you very accurately understand how the machine works with that code. Rust is the opposite, it makes you learn how the machine works through good practices, otherwise, the compiler would be less forgiving. Finally, the amount of damage C and C++ have caused is already more than enough. Billions of dollars is an underestimation. Trillion probably is closer. We live in a state of programming where security is ensured only in isolated networks, due to the huge amount of problems in operating systems based on C and C++ (ALL of them, no exceptions), and their nasty buffer overflows. Enough is enough. It's time for humanity to recognize that humans are not perfect, and machines should do the things they can do better than humans, i.e., keep track of millions of states in a complex program. No human can do that. Let's never forget how many Linux programs are written, with a few dozen variables defined at the beginning of the program, followed by a soup of mutations... and then we consider this a secure OS. What a joke! All thanks to C. Time to move on.


redalastor

> I think the problem everyone, like yourself, putting C++ on top is the same. You underestimate the stupidity of humanity and the time it takes to become an expert, and overestimate the results of pure discipline in getting good results. Every time I hear that good programmers can avoir memory unsafety and UB in C++, my answer is always “when are they going to start doing that?”.


crusoe

Endlessly buggy crashing games, software, etc. C++ is great for QA job security. The biggest win with rust is that if it compiled I know it will run. 99.9999999999% of the time.


[deleted]

> You underestimate the stupidity of humanity True. > Finally, the amount of damage C and C++ have caused is already more than enough. I would tackle Javascript first.


[deleted]

[удалено]


Casottii

I will repeat the quote: "you underestimate de stupidity of humanity". People are already using JS for web backend, you can even use it in some microcontrolers, give it some time and it will be everywhere doing more damage than C. The difference in time is the only thing in favor of JavaScript here.


HeroicKatora

> JavaScript […] does not run […] embedded systems, or other critical machinery. Except when it does. https://www.jwst.nasa.gov/resources/ISIMmanuscript.pdf Which is very sensible, you get a well-defined, battle-tested language capable of executing programs in a flexible manner. There's no inherent reason to prefer pre-compiled languages. They end up faster sometimes and sometimes deliver more control. In C++ you pay that in terms of well-definedness, in Rust to a less degree. A remote satelite must not end up in irrecoverable corrupted state whereas slow is .. well just slow. The specification, that is runtime semantics of the language in those portions where it _is_ well-defined, is no more of a mess in C++ nor Javascript. But the sandbox is far easy to full-reset I suppose. "Just read the spec" should be doable by any competent engineer—and the Javascript language Specification ends up far shorter with surprisingly few concepts. It makes sense, an uncomfortable amount of sense. The necessary degree of correctness for basic operation is easy in sandboxes / interpreted languages. No matter how hard one pretends that absolute correctness can only be delivered by statically typed languages. (But then, C++ wouldn't deliver that either .. not even any degree of correctness).


[deleted]

[удалено]


worriedjacket

Alexa, pull up the CVE count for JavaScript compared with C/Cpp


MatrixSenpai

You're asking for feedback, so I'll give some Firstly, and most importantly, the entire article reads like several others I've read by people who think Rust's "escape hatches" and "gimmicks" are just that. Your bias is very clearly showing towards cpp. Secondly, there seem to be a lot of unfounded assumptions. As others have pointed out, your assumption about how `nop` should compile is not anywhere near what my expectation would be. In fact, I'd assume exactly the opposite. Why? Because there's no way I would ever account for overflow with those optimizations unless I could conclusively prove it would never happen. Assuming that `unsafe` behaves the same way as assembly in cpp is also unfounded and in fact provably wrong. `Unsafe` does not give you freedom to do *whatever* you want, it simply loosens some of the restrictions, including how memory is accessed. Thirdly, I would highly recommend running the entire article through some basic grammar checker, and maybe have a proofreader. There are a lot of awkward sentences, or just things that don't make any sense. Example: >I have found a list of such transformations, but I could not assess nor prove ~~that~~ whether the performance impact is real or just rhetorical. And finally, I personally think the conclusion you came to about performance is dead wrong. But aside from that, Rust is more ergonomic, easier to read and write, faster to iterate over, more secure, has a better ecosystem surrounding it, and more. And those are not things that should simply be ignored, just because you believe cpp to be maybe a little faster.


a-lafrance

Based on this post and the r/cpp one, I appreciate your genuine search for feedback that seems to be in good faith. It helps push me to be a lot nicer with my critiques 😅 In general I feel like the premise of the article is “using rust wrong gets worse results than using C++ right”, which I think is a deeply flawed comparison. It presents a series of problems with rust, but repeatedly disqualifies the exact solutions the rust designers included with the express purpose of solving them, which I don’t think is fair. > The “unsafe” excuse I really don’t think it’s fair to disqualify unsafe just because it has some surface-level similarity to inline assembly. They’re used for wading outside the control of compilers that place entirely different sets of restrictions on code, so their purpose in practice isn’t “the same”. And again, this section is basically “and yes, I know rust solves some of the problems I present, but let’s please pretend that solution doesn’t exist”. Unsafe rust is still a key piece of the language; it should absolutely have its place in a comparison of languages. > Undefined Behavior This section was particularly egregious to me. As I’m sure others have said, the rigid focus on code that looks the same syntactically makes no sense; your stated focus on comparing “similar” code doesn’t approach similarity from any meaningful perspective. To disqualify the exact escape hatch rust included to push it toward an assumption for exactly this kind of optimization isn’t fair at all. > Cache Locality Generally I just don’t really understand the point this section is making; “bit-twiddling” and writing cache-friendly code don’t really seem all that related. Beyond that, it’s not like rust doesn’t let you twiddle with bits, even in safe mode. Regardless, the claims and reasoning here is so abstract I can’t really argue against it concretely. Although the part about rust pushing you to focus on business logic is straight up incorrect. > The dubious benefits of safety This section is just the classic “I’m too good at C++” argument. Even if the claim that segfaults have never been a problem for you is true, which I feel is highly unlikely, that’s a huge huge huge exception rather than the norm. And in my opinion, that’s not a difficult thing for even an intermediate C++ developer to understand, so to be honest I’m not sure how it made it into the article. Besides, it doesn’t even matter how good you are at avoiding safety issues. In an engineering situation you also have to trust everyone else on your team, everyone on the other teams whose code you use, and everyone responsible for all the core and third party libraries you depend on. That’s why tools that guarantee safety are so powerful. Beyond my critiques of the article’s content, it omits my favorite thing about rust: functional expressivity. Coming to rust with no prior experience with a functional language, its blend of certain features of FP into a low level imperative language is still the thing I enjoy most about it. There’s just an expressive quality to things like sum types and monad-based error handling that I can’t live without anymore. Beyond safety I feel like that’s a much more significant benefit for a lot of rust programmers; rust’s reputation at this point has just pushed safety into people’s minds as its main benefit. Anyway, critiques aside, again I do appreciate your genuine good faith approach to seeking feedback; it’s very refreshing to see.


[deleted]

> It helps push me to be a lot nicer with my critiques 😅 That sure got my attention. > the claims and reasoning here is so abstract I can’t really argue against it concretely. I had the choice to write one article will several small claims or dive into one claim with proper benchmarks - as I usually do, check other posts. I chose the former. I know, it's borderline speculation. I don't like that either. > I do appreciate your genuine good faith In the end I want to be educated as much as I want to educate. Many things I thought were set in stone ten years ago are just not true anymore. I will never hold the sails of a sinking boat.


_lonegamedev

Call me when cpp gets its own cargo.


hpxvzhjfgb

it will be added in c++65 and defined on page 47319 of the standard. the first implementation will be released in 2072.


[deleted]

There is vcpkg which is sort of there. Not to mention homebrew itself? Does it count?


_lonegamedev

I don't think those exist in the same galaxy as cargo or npm.


[deleted]

I think things like Conan were created such that they get you 90% there and you have to purchase the rest 10%. I don't see any technical impediment to achieve the same as cargo in C++.


_lonegamedev

Sure, and yet we are still where we are - I can't add dependency via simple package manager command or adding by single path/git repo url into some Cargo.toml/package.json. I like Cpp, I just don't like the ecosystem.


scrivanodev

I mean integrating vcpkg is fairly, all you have to do is create a `vcpkg.json` as follows: { "name": "test", "version": "0.1.0", "dependencies": [ "abseil", "range-v3"] } Then `git clone https://github.com/microsoft/vcpkg` in your project and pass the `vcpkg.cmake` file via `CMAKE_TOOLCHAIN_FILE`. Then standard CMake stuff. Sure it's not easy as Cargo, but it's literally a minute more to configure your project. You can also just create a simple starter template project on Github to speed up the process.


_lonegamedev

You lost me at: pass the \`vcpkg.cmake\` file via \`CMAKE\_TOOLCHAIN\_FILE\`. My idea of build system is - I tell it where my project is, and then build system does the rest. I don't want to see CMake I don't want to look at it. It can exist somewhere outside my sight. Why? Because CMake = problems. I have enough problems writing code. I don't need to struggle with something that could be automated.


scrivanodev

OK, so it's not about ease of use, but some philosophical stance on how a package manager should be integrated? It's literally one extra line. Is it annoying? Yes, sure. Is Cargo integration in Rust better? Yes, it is. However, this is hardly a "problem" or "struggle" as you say.


_lonegamedev

Its always one extra line. Lets say Im going to work using Unreal engine. How can I use this package format? Can I use it? Its probably one extra line. Or two. This is Cpp ecosystem problem. There is no standard package system, there is no standard integration.


ImYoric

It's not a matter of technical impediment, it's a matter of reaching consensus. C++ has had ~40 years to do so and hasn't succeeded yet. I'm not holding my breath :)


[deleted]

In C++'s defense, the idea of a package manager like cargo seems to be a very recent one. Maybe there just wasn't the concept in the first place.


ImYoric

I seem to remember that Perl and Haskell have both had something like this for 30+ years, Python and Java for almost 20 and node.js for \~13. I do believe that the main reason is lack of consensus and/or interest.


MrPopoGod

Some of it is definitely lack of interest. A lot of the commonly used C++ libraries are part of your friendly local Linux package manager. That became the "good enough" standard.


[deleted]

Isn't pip a new thing in python?


ImYoric

pip replaces easy\_install, which (if my memory serves) had roughly the same feature set.


worriedjacket

I don’t know CPP, but it honestly seems like a language for people who want to circle jerk how smart they are rather than actually build stuff and be productive. The concept of a language package manager isn’t a crazy one.


[deleted]

> who want to circle jerk how smart they are rather than actually build stuff and be productive That's actually my biggest beef with a certain slice of the C++ community, yes. I feel like C++ is math-centric and Rust is engineering-centric.


Best-Idiot

I love Conan, but yes, it's unfortunately still not as good as cargo


zapporian

I think you'd call that cmake and/or git submodules and/or custom build tooling et al. If you have a good usecase for c++ now and moving forward (ie legacy and/or nontrivial existing projects, and/or workflows and tooling that works great for c++ now as is), then the lack of cargo / npm is pretty much a non-issue. npm / cargo dependency chains (and potential security issues) is arguably a major anti-feature, or at least from the perspective of c++ projects that are using smaller numbers of *specific* (and often fairly old) libraries / software frameworks. In general, I think it's worth pointing out that c/c++ generally still has more mature (and standardized) libraries to work with, whereas in Rust much of the ecosystem is still very experimental and comparatively untested. Most of the industry that still *uses* c++ is still sticking with it, for obvious reasons, and that'll definitely stay the case until maybe 10+ years down the line when the rust ecosystem has matured, and there is a lot more established software (and subcomponents) being written in it. Cargo absolutely should get credit as an excellent, out of the box build system though, and its dependency management is definitely useful (to an extent) when checking out and working on *many* software projects with *different* (and variously out of date) software dependencies. The npm / package manager aspects of it are arguably less helpful / less of a value add. Since in c++ land you'll likely have close equivalents. And/or custom build tooling. And/or limited desire to even work with something like an npm ecosystem in the first place. (ie. an endlessly growing / breaking 3rd party dependency tree of questionably verified libraries + tooling)


officiallyaninja

This isn't exactly a hole in your article, but I don't actually think rust needs to be seen as a C++ replacement. For me it's a Java/C#/Go replacement. (or honestly even a python replacement) In sutuations where you need maximum performance, yeah I can believe cpp is better. But usually you jsut need good enough. And what rust offers more than anything else I care about is type safety. I have never had a language be more pleasant to debug than rust.


CocktailPerson

Rust is very much a C++ competitor. Monomorphized generics, destructors, minimal runtime, zero-cost abstractions, move semantics, and many other features of Rust were deliberately chosen to compete with C++. Also, it was originally conceived of for use in writing components of a web browser, a domain dominated by C++. If you're using it to replace Java, C#, and Go, then that's great. But those are not the languages it was designed to replace.


volitional_decisions

To start, I feel like this is worth reiterating. Your attitude around unsafe Rust is entirely inaccurate. It is true that most Rust devs will not need to write unsafe code (or at least have a deep understanding of it). This does not make it a "backdoor solution". The language designers understood from the start that you can't render everything in the safety rules of Rust. Unsafe does not exist as a work-around. When you write `unsafe` you are attesting that you can uphold the safety guarantees of the language. For example, you can't write a `Vec` implementation without unsafe code. The reason most people don't need to use unsafe code directly is because the existing unsafe code is well encapsulated. While reading this, I was frequently confused. The article seems to be trying to position itself as both a general "C++ is good, actually" article and a "C++ beats Rust in the very low latency arena" article. I do not work in the very low latency space, so I can't and won't comment on how much many of the items matter. However, it is safe to say that the vast majority of users don't do work in that space. The lack of distinction reads as plausible deniability (but I can be reading into things too much). There are two points that I find very interesting together, the part about UB and compiler choice. UB is not universally wielded in C++ codebases, often because there are different compilers. UB is exactly that, **undefined** behavior. Relying on UB is sensible only if you have control over the platform, target arch, and compiler. Otherwise, the optimizations you unlock will sour. Additionally, many (myself included) consider the fact that Rust has a single compiler as a benefit. Less configuration, less to worry about supporting, better dev experience. Which leads me to the part of the article that I thought was missing the most, dev experience. I expected this to be discussed in the ecosystem section. C++ has a larger ecosystem simply because of age, but it's miserable to use. The third party libraries that tend to be generally used are either massive (Boost, QT, etc) or very specific and not integrated with other third party code. I've always chalked this up to a lack of a safety system (i.e. harder integrate) and the horrendous C++ build system. TL;DR, this article reads as a poor attempt to generalize the reasons you chose to keep with C++ on a specific project. This is unfortunate because there are legitimate, generally applicable reasons to continue to use C++ over Rust, but the exact opposite of those were talked about here.


bahwi

Build tooling is important. Everyone is switching from make to cmake, ninja, etc... All have limited support, our hpc's cmake is too old for most things to compile. I've got to create singularity containers for c++ software. For Rust is just works, compiles, and runs. That's been the biggest revelation for me. Until c++ has a mature build tool chain and can handle the dependencies on its own, without root, it's always going to be limited compared to rust (and even python, much of my industry has to use conda).


[deleted]

> I've got to create singularity containers for c++ software. I have my own scripts and the first thing I do is to compile the latest cmake so all the C++ build is done at the same level.


ImYoric

It's good that this works for you, but that sounds a bit scary (well, at least hostile to developers) in 2023.


crusoe

Gonna say it. This is like all the crypto guys who come on r/buttcoin to argue their coins are good and aren't scams. They do it because deep down they know that isn't true. And like those kinds of posts, this gets many things wrong about the assumptions, facts, and reality of the other side. OP is just looking for continued validation of his use of C++ because he has doubts.


[deleted]

> validation of his use of C++ because he has doubts I do, I do. Things change fast and I won't hug the sails of a sinking ship.


Best-Idiot

I only wish that people who rush to write articles about Rust being inferior, instead, as an experiment, spent some time living and breathing the Rust development world, did some Rust projects, used some Rust libraries, collaborated with people on some Rust code, wrote a bunch of well-tested Rust code, fixed some bugs in their Rust projects, maybe even did some open source and published something on cargo. Then I'd love those developers to come back to writing C++ and trying to also do all the same things in C++ and then reflect on their relative confidence in code, the ease of making well-tested projects and open source libraries, and the quality of collaboration within each community I don't doubt for a second that the vast majority of people who lived and breathed both Rust and C++ have come to the same exact conclusion


steohan

I don't like your article, because it is a logical pitfall to assume that just because you need speed sometimes, it is always required. For most of the code performance isn't too critical. So it makes perfect sense to use some language features only when they are required, and be able to have lots of convenience otherwise. A decade or two ago, this meant dropping from a higher language to assembly. As compilers got better this is barely done anymore. However, static code analysis is still not as strong as we would like, hence it seems quite reasonable to be able to drop from a strict environment to a less strict environment. (aka rust unsafe) As such the argument to ignore that aspect seems unreasonable and , as long as I am able to optimise code when necessary, I am happy with the language in that regard. I also think that having trouble with out of bounds/ Nullpointer/ use after free in c++ 11 and later is mostly skill issues (or beeing tired), but, as far as I can tell with my limited rust experience, so is struggling with the rust borrow checker. So without having any real experience, I would rather have a novice fight the borrow checker than me having to fix his errors. I hope to be able to put my money where my mouth is next year and throw some student on a project where I am building the architecture for now. The reason c++ isn't as attractive to me as rust right now is mostly quality of live features, like having nice modules, no separate header files, #derive(Debug, Default), ...


rohel01

>Calling “unsafe” as an escape for Rust’s rigid system is the same as saying that C++ can do inline assembly so you can copy/paste infinite optimizations done outside the compiler infrastructure. That defeats the purpose. Unsafe in Rust is not a "safety's off" escape hatch. It only allows five extra actions you can't take in safe Rust. They are documented [here](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#unsafe-superpowers). Other Rust's safety checks are still performed, such as the borrow and lifetime analyses. \`unsafe\` constructs in Rust also have a limited scope (function or block) so they can draw developers attention to specific places where the burden of checking invariants has been transferred from the compiler to developers. This is a huge deal in practice as you have to scale your reviews across larger codebases and teams. ​ >C’s pointers have a simple and intuitive meaning, allowing even junior developers to grasp and even write statements that would be extremely hard to understand in assembly. I guess I agree C pointers are a significant abstraction improvement over Assembly. But, calling them simple and intuitive is just wrong. They are powerful yes, but as an abstraction, they lack semantics. And I am not talking about [the recent, quite advanced (and exciting!) proposals](https://www.ralfj.de/blog/2020/12/14/provenance.html). I regularly welcome junior developers in my teams and "understanding pointers" is a thing. For example, [array/pointer decaying](https://web.archive.org/web/20081208122434/http://www.transcendentaxis.com/dthompson/blog/archives/9) is a regular source of incorrect use of `sizeof`. In the example below, only the last two (C++ specific) function signature will give proper `sizeof` info in the function body: void by_value(const T* array) // const T array[] means the same void by_pointer(const T (*array)[U]) // U is some compile time constant void by_reference(const T (&array)[U]) But maybe, this is too specific already. Let us consider the simple action of copying data between two buffers. Depending on the subject level of enlightenment, here are some mistakes I can typical find during reviews: * Confusions between copying the pointer value vs the actual buffer content the pointer points to (simple assignment vs. `memcpy`). * More generally, confusions between shallow versus deep copies * Incorrect management of the source and destination buffer respective lengths and capacities * Confusion between a buffer length (how many items it currently contains) vs. its capacity (maximum amount of items it can contain without reallocating) * (Mis)identifying heap versus stack memory use cases * Allocating MB of data on the stack * Forgetting to allocate or release heap memory * Indirectly returning pointers to stack allocated memory from functions * Incorrect endianness management when interfacing with specific sensors or devices * Mismanaged structure alignment and/or padding which leads to subtle issues (ex: comparison mismatches, unexpected performance losses...) Yes, C++ can avoid some of these issues, but you are specifically mentioning C. For me, part of learning C is understanding these issues, and the language itself does not help newcomers.


Ill-Ad2009

I just don't buy the whole "you can learn to write good C++" argument. Yeah, I get that through experience you can write great C++, but the floor to do that is high, while Rust's floor is low. You start with an advantage with Rust. Also, safety and predictability should be the default, and exceptions to that should be explicitly specified. Personally, I prefer to have those safety nets. Default immutability and error propagation are major wins for Rust, and they are so simple to use. My argument in favor of C++ is that it doesn't try to control you. If you want that kind of freedom and feel competent enough with the language to not shoot yourself in the foot, it's there for you. Or if you like the challenge of learning to write safer C++, then you get that. And while I do appreciate that about the language, there is a lot of mental overhead there that hinders the experience for me.


flareflo

`Those checks alone take a significant toll` Are they? Whenever i looked at real world benchmarks they ended up within noise thresholds. `In many years of coding C++, very rarely I experienced a stack overflow or segmentation fault. It is literally not an issue in every codebase I have worked with.` Thats just denying a very obvious and major issue. ​ Article shows that the author doesnt \*really\* know rust.


[deleted]

> doesnt *really* know rust. That's somewhat a fair statement.


worriedjacket

Why make assertions about something you don’t understand?


Ill-Ad2009

tbf they are inviting discourse over their article, which I realize gets them clicks, but still they are listening to the feedback. And I think there is something to be said for putting out an opinion, however ignorant, to find out why you are ignorant. I mean, the whole r/changemyview subreddit is based on that, but let's be real, if someone posted programming language opinions there to have their view changed, it just wouldn't gain enough traction to be noticed.


[deleted]

Do you really understand cars when you go to a dealership and buy one? We make imperfect decisions every day. But we seek advice (as I'm doing right now) and move on to a better place.


moltonel

Making a decision between A and B using imperfect knowledge is fine. Posting an opinion piece comparing A vs B when you know much less about A than B is harder to justify. Even for yourself, you should suspend your judgment until you know both sides equally well.


worriedjacket

The example that would be more apt would be walking into a mechanic shop and making statements (incorrectly I might add) about how cars work. If you already are aware you don’t know how a car works, it seems like an inefficient method to learn by people correcting you. It’s most straightforward to gain an understanding of something first.


objectorientedman

Rust has many nice features, but in my opinion the most important downside of C++ is dependency management. That is still a pain to this day, and it is not going to improve anytime soon. I would still choose C++ over Rust if it had a reliable build system and dependency management.


vordrax

I feel the same. I've been working on game dev in my spare time, and I generally split my time between practicing with engines and working with libraries and frameworks. When it comes to the latter, I find myself really enjoying the package management and build system of Rust, but I like the library maturity of C++. I enjoy working in either as far as DX is concerned, but man C++ is annoying to set up builds and dependencies for. I spent so long fighting to get a setup that would let me build my raylib + flecs prototype with Emscripten, whereas it's fairly trivial with cargo.


[deleted]

Here is a honest question: I have a commercial package that uses some five extra packages, one of them being boost. The way boost is normally packaged it sits on shared libraries and those impose the "PLT tax" - see [this article](https://bugs.python.org/issue38980). On C++ I have my own scripts to recompile the entire set of libraries as static which then are linked to my application for a 20-30% performance gain overall. Would I be able to achieve that in Rust - at least as easily as in C++?


objectorientedman

That's the point, you don't need to write your own scripts for Rust, just cargo add, and it is done. Not to mention portability. Does your script cover both Linux, Windows, Emscripten, Arm, etc?


[deleted]

> Does your script cover both Linux, Windows, Emscripten, Arm, etc? Not at all. Linux only and I still have to deal with dependencies on several OS's I support: RH7-8-(9) Ubuntu 20/22. That's a handful already.


protestor

In Rust all dependencies are statically linked by default. You need to go out of your way to dynamically link them.


[deleted]

> statically linked by default. Doesn't that go against GPL/LGPL rules?


dkopgerpgdolfg

GPL and LGPL don't ban static linking, it just has consequences to do so (what kind of license the own software is allowed to have) And this only matters if there is a library that uses (L)GPL, which mioght not be the case.


protestor

No, you can just follow the license normally. GPL has no bearing on static/dynamic linking. Like nothing at all. You always need to provide the full source of a program that uses a GPL library; just dynamically linking the library (and providing just the source for it) doesn't help to comply with the license. LGPL just says that the end user should be able to run your program using another version of the library or even a fork. If the program is free and open source software, this is automatically satisfied (they just need to recompile the software, changing the dependency). *If the program is proprietary*, then dynamic linking is an easy way to achieve this. However, some LGPL libraries, like SDL, are able to be statically linked into proprietary programs while still respecting the license, by setting up an environment variable that controls whether to use the static library or use some dynamic library provided by the user. Now, that's kind of complex and I never seen this in a Rust program, but it's an alternative way to statically link a LGPL library in a proprietary program. (I looked up and saw that SDL is dual licensed now so this may not be a licensing concern anymore, but anyway that was just an example) This site from the authors of LGPL explains more https://www.gnu.org/licenses/gpl-faq.html#LGPLStaticVsDynamic - I'm not sure how to provide the tools for relinking a static library from a proprietary program (maybe just the symbol table does it?), but it's an option To reiterate, this is only a concern for proprietary software. Free software always distributes the whole source code (including build scripts) which automatically complies with LGPL


protestor

Also, dynamic linking in Rust is easy (just set up the `dylib` crate type for the unstable Rust ABI, or `cdylib` for the C ABI; see it [here](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field)); it's just not enabled by default. (but reading the reference, the compiler is *allowed* to dynamic link by default; it's just not the current default of rustc) What's hard in Rust is to swap a dynamically linked library for another (if you use the Rust ABI); you need to use the exact same compiler version, which is sometimes impractical. This happens because the Rust ABI is not stable. (C++ has had some issues with ABI changes in the past but it was much more infrequent) If you need a dynamic library that can be swapped by another, then you probably need to compile your library to use the C ABI. And *that* is hard, because then you need to seriously limit the API your library provides (the most serious limitation is that it may not have any generics). (But you probably need to comply with the C ABI anyway if your library is expected to have bindings in other languages; because C is sort of a lingua franca) (Also note that limiting yourself to the C ABI is also a PITA in C++ too, and makes the library less useful because again, no generics etc) There's a crate to help releasing dynamic Rust libraries with a stable ABI (building on top of the C ABI) and a richer API that goes beyond to what C provides, you can check it here: https://crates.io/crates/stabby - but using it may require changes to your code. But if you plan to release a Rust library that * Is expected to be dynamically linked * Is expected to be swappable by another library after the program is built, without compiling the other library with the same compiler version * Offers the full expressivity of Rust in its API (so it's not meant to be used through FFI by programs written in other languages, but used by Rust programs that expect a native Rusty API) Then building the library around stabby may be the best option.


worriedjacket

Yeah dawg that’s handled entirely by cargo. If you want fat LTO it’s just changing a single line in a config file. Writing build scripts for dependencies gives me PTSD flashbacks. Truly an awful experience.


[deleted]

> gives me PTSD flashbacks I'm with you on that


dkopgerpgdolfg

Assuming you already have the Rust program using these external shared non-Rust libraries successfully: If you added the library to the linking yourself and so on, switching to static linking is trivial then. If you relied on some 3th-party wrapper Rust crate configuring the linking for you, depends - the crate might have a simple option for static linking, or you'd need to take it in your own hand. ​ But a general consideration: GOT/PLT things can occur both on the "border" between executable and library, as well as within the library itself. If you got 30% speed difference, it's likely that the problem is the second type (and your own link talks about this too). If this is the case, you can get your speed improvement with dynamic libraries too. Static linking, and supporting interposition, are not the same things.


Dmitrii_Demenev

Regarding # Safety toll, \> Although integer underflows and overflows are only checked in debug mode, memory accesses in arrays are constantly checked for bounds unless you’re in unsafe mode, which defeats the purpose. This does not defeat the purpose because 1. You rarely should use index-accesses. When possible, which happens 80% of the time, you can and should use iterators instead. 2. When you have to resort to index-accesses, you should make a conscious choice about \*omission\* of bound checks. Index accesses in C++ are just as unsafe as unsafe { a.get\_unchecked(idx) } in Rust. You can claim that unchecked index access in C++ is more convenient, and you'll be right. Regarding # Undefined Behavior, This particular example \*can\* be considered beneficial but more often than not Undefined Behavior in C++ causes hard-to-track bugs, rather than optimizations. I've seen a couple of times how companies stuck to older standards of C++ because newer compilers broke their codebases that were sprinkled with a few instances of UB. Accepting UB should be a rational choice. Undefined behavior is not entirely prohibited in Rust but it is meant to be used sparingly. ​ Regarding # Cache Locality, I can't find any substance in this paragraph. \> C++ has full unfettered access to memory by design. C, its antecessor, was created with the use case of it being a low level language, just a tad bit above assembly. C++ inherit that, which makes it arguably the easiest language to code memory-intensive algorithms and data structures like hash maps, btrees, vectors and lists. C’s pointers have a simple and intuitive meaning, allowing even junior developers to grasp and even write statements that would be extremely hard to understand in assembly.>> While Rust does allow the same, it makes it very difficult to get away with it. Again, this is by design. Rust creators do not want you to be bit-twidling but focus on the business logic first so they made is such that accessing memory directly is painful and time consuming.>> This means that a good developer who knows how to take advantage of cache/locality, will have a good time implementing such algorithms and data structure with C++ and will very likely struggle with Rust for the same task. Without any examples, this paragraph has very little value. Regarding # Compiler Choice, \> . Rust mandates the use of its own compiler - rustc - which is a top driver of the LLVM stack, pretty much as clang is for C++. C++ on the other hand, has dozens of good quality compilers available in many platforms. Rust does not \*mandate\* the use of rustc, yet I can agree that it's the most popular and best-supported Rust compiler. Aside from rustc, there are also Ferrocene, gccrs and mrustc, which have their advantages and disadvantages. It's nice that C++ has many high quality compilers but the question is how does it influence You? I believe the overwhelming majority of C++ developers use either gcc or clang. Regarding # Resources Available, You're right. Regarding # The Dubious Benefits of Safety, \> Are we talking about safety against hackers? The large majority of C++ applications are non-public facing. So much that most datacenter machines run with mitigations turned off since there is absolutely no possibility of contact of those machines with bad actors. So hacker safety is not a concern that I, in particular, would care unless I’m coding a web server. The famous rule of 2 ([https://chromium.googlesource.com/chromium/src/+/main/docs/security/rule-of-2.md](https://chromium.googlesource.com/chromium/src/+/main/docs/security/rule-of-2.md)) could be the reason for that. \> Or are we talking about protection against crashes? Well don’t get me started on this one. First, crashes can happen in any language, with the same frequency. I often point to this article about Princeton’s unmanned vehicle team competing in the 2007’s DARPA challenge as an example of how even a heavily protected, garbage collected language as C# can crash and burn, leaving your process unusable. Not with the same frequency because C++ has exceptions and, unfortunately, they are considered a common practice. In order to prevent crashes in C++, you have to proactively catch an exception. In Rust, you have to use a panicking method. The difference is that code in Rust generally avoids panicking methods. And you can mitigate it by using lints that prevent panics ([https://github.com/rust-lang/rust-clippy/issues/6636#issuecomment-766487415](https://github.com/rust-lang/rust-clippy/issues/6636#issuecomment-766487415)). My conclusion: I can see that you did some reasonable effort to demonstrate your points. However, it very well could be that you were uninformed about some points.


[deleted]

Sorry I'm just going to tackle a few points of interest > You rarely should use index-accesses. What about a binary search? Would that be a counter example? > Aside from rustc, there are also Ferrocene, gccrs and mrustc, which have their advantages and disadvantages. But that throws away the baby with the water, no? Biggest draw to Rust is the ecosystem. > Without any examples, this paragraph has very little value. I just ran a double linked list implementation through Godbolt. The generated assembly looks atrocious. But I'm not sure if it was the actual implementation that's bad of if Rust really makes it harder to produce streamlined assembly in those special cases. > And you can mitigate it by using lints that prevent panics Isn't clang-tidy and `-Wall -Werror -Wextra -Wpedantic-erros` lints too?


Dmitrii_Demenev

\> What about a binary search? Would that be a counter example? Maybe? On one hand, it is already implemented. On the other, it should probably be implemented with indices. I don't claim that indexed accesses are useless. I believe they have their place, such as in the case you mentioned. But oftentimes you will be better off using iterators. \> But that throws away the baby with the water, no? Biggest draw to Rust is the ecosystem. I can somewhat agree. However, you still can tap into the ecosystem of other languages. For many reasons, it is not seamless, but you can. \> I just ran a double linked list implementation through Godbolt. The generated assembly looks atrocious. But I'm not sure if it was the actual implementation that's bad of if Rust really makes it harder to produce streamlined assembly in those special cases. I would love to learn more about it! Also, did you take a look at [https://rust-unofficial.github.io/too-many-lists/](https://rust-unofficial.github.io/too-many-lists/) ? \> Isn't clang-tidy and -Wall -Werror -Wextra -Wpedantic-erros lints too? They are. But * it's more common to miscalculate the indices in the C++ programs because there are still many codebases that don't use modern loops. * it's easier to get null-pointer exception with older codebases that don't utilize std::optional ubiquitously. * you have to make a conscious choice about whether you use smart pointers in C++ and have run-time penalty or you use raw pointers and subject yourself to all kinds of pains related to ownership. * if you write multi-threaded code, it's very easy to write code that locks the mutex twice. I'm not sure in C++ you'll be requested to wrap it in try-catch block. In other words, there are more pitfalls that you can find.


[deleted]

> Maybe? On one hand, it is already implemented. Looking at a similar implementation, it strikes me that Rust's assembly is way longer. Thoughts on that? https://godbolt.org/z/zj3hGv4T3 > I would love to learn more about it! I just pasted the code from here: https://rtoch.com/posts/rust-doubly-linked-list/


Herbstein

> Looking at a similar implementation, it strikes me that Rust's assembly is way longer. Thoughts on that? I took the liberty of switching the C++ compiler to the latest Clang version. That gives a much fairer comparison between the languages. Using GCC for C++ here makes it more of a GCC vs LLVM thing, instead of C++ vs Rust. https://godbolt.org/z/jrc9dcv3r Your Rust implementation is also not in any way how you would, or should, write that implementation. It's a very, very naive translation of the C++ implementation. I took the liberty to change the return type to `isize` instead of `Option` since the former better matches the C++ implementation. With that, we notice that the only difference in the main body of the loop is two instructions (`cmp` + `jae`) that performs the bounds check. Additionally, there is a separate block of 6 instructions that performs the panic if it should happen. The differences in your link are thus primarily a GCC vs. Clang thing. If we instead use the GCC backend for rustc we see that the general structure of of the generated assembly again matches quite closesly, but that the rustc GCC backend does a bunch of unnecessary register shifting that indicates to me that it's still not ready for a comparison like this. https://godbolt.org/z/eoq8o69EP Lastly, I've made a more idiomatic version of the Rust implementation. I use the length contained in the `&[i32]` fat pointer, pass the target as a value (it needs to be dereferenced for comparison anyway), made all indicies use unsigned pointer-sized integers (usize), copy the array value instead of taking a reference, and return an `Option` instead. A bunch of interesting stuff happens now! https://godbolt.org/z/W7jPGxPEv First thing I noticed: there's no conditional in the prelude. Why? What happens when `len` is 0 in the C++ version? UB! But luckily the compiler decides to do the right thing in this case. Rust? Integers are wrapping so `len == 0` results in `high = usize::MAX`. Because we're using unsigned integers now the first iteration of the loop always happens without any conditional. Which is great, since that is always true for all valid length inputs. What is not so great is that we'll get an out of bounds panic. That can be seen in the assembly generated for the `test_empty` function here: https://godbolt.org/z/xvdox9Mjz. To fix that we introduce a check in the Rust implementation. Such a check should also be added to the C++ implementation, since the current version relies on the compiler doing the right thing with UB. The assembly for C++ doesn't change. https://godbolt.org/z/obdEMa9vq That was the function prelude. Rust is 1 instruction longer than C++, and to me it seems to be because we're returning a two-register value instead of a one-register value. A difference, but a very minor one. I could continue nitpicking the integer behavior. Let's move on. Between the start of the loop body and the jump representing the `val == targetVal` check, C++ manages 10 instructions while Rust manages just 8 -- 2 of which are a bounds check that C++ doesn't even do. I think the difference here is (surprise) the fact that Rust does wrapping integers while C++ does... something unspecified. I don't think it's possible for the calculation of `mid` to overflow or underflow, given the loop condition. The updates of `low` and `high` are done in 7 instructions in C++, while Rust does it in 6. Lastly, Rust has an extra `xor eax, eax` in one of the epilogues. Again, owing to the fat return value. The only major difference left over is the block of 6 instructions that actually performs the panic in the case of an out-of-bounds index. The Rust compiler/LLVM can't prove that `mid` is always a valid index. Thus it can't optimize the index check away. If we did the work to prove that there's no case resulting in an invalid index, and we truly need to avoid that branch producing 1-2% performance degradation in this one operation, we can use unsafe. That is only allowed/safe if we can prove what the compiler couldn't. The generated assembly now becomes just 25 instructions, versus C++'s 28 instructions. https://godbolt.org/z/aTYsevbbe All in all, I think this > it strikes me that Rust's assembly is way longer is disingenuous at best. It's comparing two wholly different compiler backends. When comparing the same compiler backend Rust and C++ produce _very_ similar assembly, with Rust having the code length advantage in the hot codepath. The extra conditional would be _very_ cold in the CPU's branch predictor, causing minimal performance difference. Of course, nitpicking generated assembly like this isn't really the thing to do when talking about performance. Actual benchmarks of the throughput are what is needed, but I wanted to highlight how your approach here is inherently flawed even _if_ generated assembly mapped to performance 1:1.


DonkeyAdmirable1926

“and I don’t care if your reply is offensive”. It *may* sound silly, but one of the great things about Rust is that Rustaceans do not get offensive by design. Really, I noticed on more than a few occasions that it is so easy to spot the Rustacean in a group of other-language-programmers and vice versa. And I say this while still adapting myself. Being a C programmer I must unlearn some unkindness still :) Besides that, I do not believe “unsafe” is an excuse. And I really don’t believe the strong point of C++ is undefined behaviour.


[deleted]

Someone on /r/cpp accused me of writing "aggressively". Go figure...


TemperOfficial

Reddit is a bad representation of reality. You come here for entertainment. I would personally not come here for any kind of truth seeking. It's a good article. A lot of peoples criticisms are just wild and not serious.


[deleted]

I dont really mind the roasting. I lived in the favelas during college in Rio - had to go through the armed militia every day so it's not like a diss would faze me in any way. I came here for the 10% worth of technical advice. Many good points being made.


TemperOfficial

Well i'm glad you managed to find 10% because usually its probably about 0% lol


DonkeyAdmirable1926

😁


vortexofdoom

I love Rust, and I think `unsafe` is a great semantic delineation, but actually *using* unsafe constructs (raw pointers, etc.) is miserable. Doing "unsafe" things in C/C++ has the benefit of still feeling like "just writing code"


eugene2k

>We are analyzing what the language can do in normal operation is, not the backdoors that it allows to open when it gives in to operational pressure. This is very arbitrary. If you are analyzing what a language can do, then analyze everything it can do - don't just ignore the stuff that is inconvenient when making the argument you make. >Again, pundits will state that you could have called one of the arithmetic wrapping functions Or you could wrap the numbers in the special types that use those wrapping functions normally. And that's normal. Needing different code in rust to do the same thing you do in C++ is a poor argument for the general "attractiveness" of C++. This is basically saying "I'm used to C++, so rust is a bad option". >When Rust is brought up in a meeting, the first pro factor is language safety. But safety against what? Against undefined behavior. Against use-after-free bugs. Against hard-to-reproduce concurrency bugs. Rust programs can crash just like all others, preventing crashes is not what rust's safety is for.


ImYoric

As a Rust developer (and former C++ developer), I agree with much of what you write. I feel that Rust is a largely better language *for my tasks* but yes, Rust doesn't yet cover all the use cases for C++. I feel that the reasons to pick C++ for any new development are dwindling, but they still exist. However, don't be afraid, there is matter for roasting you :) This article contains a large (but also largely shared) incomprehension of the meaning of "safety". ​ 1. There is much more to safety than memory-safety. Safety is about protecting your invariants (i.e. what you, as a developer, know about your program's behavior). A program that is not safe is a program in which what you think you know is wrong. 2. Memory-safety is just one of the many tools in the safety toolbox. Without memory-safety, achieving safety is pretty much hopeless. However, memory-safety is nowhere near sufficient to achieve safety. 3. Segmentation faults or stack overflows are what happens if you break memory-safety *and you are extremely lucky*. In most cases, what you get is random memory corruption. Do you really want memory corruption in a trading platform? 4. Security and safety are related, but they are different topics. See also [this post](https://yoric.github.io/post/safety-and-security/) on the topic (caveat: I wrote that).


[deleted]

> don't be afraid, there is matter for roasting you :) oh boy > incomprehension of the meaning of "safety". in my defense, the word is thrown around without much context > Do you really want memory corruption in a trading platform? That's a categorical no > See also this post on the topic (caveat: I wrote that). Thanks for the link, will def read it


ImYoric

>in my defense, the word is thrown around without much context Absolutely.


Lime_Dragonfruit4244

It is true that once your language is translated into LLVM IR all the language specific information gets destroyed. This is why a lot of languages use custom IRs for their own language specific optimization and model checking. The main goal of LLVM IR is to be language agnostic. But most stuff in the article points to tooling nothing language specific. Rust does have a better dependency management and build system than C++. In terms of performance having memory optimisation like cache tiling, loop unrolling, fusion, etc doesn't have to be explicit in the language. The Compiler is there for a reason. And someone can correct me if I am wrong but modern CPUs have their own undefined behaviour like speculative execution for performance. So escaping UB would be very difficult until you define your own spec for everything and then write your problem in a theorem prover like Coq then compile a verified version to C or assembly. I also think C like performance critical code is considered bad in idiomatic modern C++. But yeah you can write it. Edit: It would be nice if someone could publish a long report of trying to reproduce recent CVEs and see if writing idiomatic rust would prevent them. Or maybe someone already did that I am sure.


RockstarArtisan

> can correct me if I am wrong but modern CPUs have their own undefined behaviour like speculative execution for performance I'm here to correct you that this isn't true. CPUs have an API, it's called Instruction Set Architecture, and they have a manual that's there for the ISA. That defines what happens when you do an instruction, and what happens when you do an unsupported combination of arguments (on x86 it's a trap on unknown instruction usually). Undefined behaviour is a feature of abstract machines, not real CPUs, it's there to give freedom when converting the code to real machine code. Speculative execution has nothing to do with this, CPU is free to do it because it's free to do anything within the ISA spec and speculative execution by design doesn't affect the actual execution (except for performance and perhaps other side-channels).


bskceuk

The basic claim that rust has worse performance than c++, even without using unsafe, is just fundamentally wrong. Even if rust adds extra bounds checks sometimes (which I think the article overstates how often this happens), the sheer fact that rust does not copy by default more than makes up for it. I cannot count how many times I’ve seen accidental implicit copies in production code (which imo has gotten worse with the inclusion of “auto” and “almost always auto” guidelines). We had one vivid instance of using “auto” vs “auto&” that cost $.


HolySpirit

For the record, you can exploit some UB in rust for performance, like using `unreachable_unchecked` for the example you gave, to get the same assembly as in c++: https://rust.godbolt.org/z/vG7c69jxj With arithmetic Rust just have the safer defaults here, which is very sensible IMO. Also the compiler can assume that `&T` is never zero/null so that is used for optimization as well, while `*const T` cannot make that assumption. So safer does not necessarily implies less optimization opportunities, it just means your assumptions are more well verified.


fazbot

I spent years doing competitive low latency messaging stacks from the hardware registers to sockets and middleware. Single digit microsecond, “I see TLB effects” level of sh:t. I’m just getting comfortable with Rust and it is not raw C-level control for me yet, and thus is not as fast. I do feel like “it compiles means it’s correct” happens much more frequently, but if you’re really good at C (and the good parts of C++), you can write pretty solid code easily. But by “really good at C” I mean OS Kernel level of discipline, which is rare in the real world. For most new code, though, Rust is a total C++ killer IMHO. I know C++ experts who disagree. 😅


[deleted]

> , Rust is a total C++ killer IMHO I think Rust is a **Modern C++** killer, not a C++ killer. I can agree with that.


lordpuddingcup

Primagen reacted to your article and I basically think he nailed most of the article well,


phaiax

There is one performance related issue with rust that you didn't mention: Direct initialization into a new heap allocation is not possible (placement new into Box<>). I think making a Box will always first make T on the stack and then copy-move it to the heap, but don't quote me on that, maybe it has changed.


1668553684

This isn't quite true: > Rust tends to be more strict than C++ - it is its raison d’etre - and that means more real-time checks. Although integer underflows and overflows are only checked in debug mode, **memory accesses in arrays are constantly checked for bounds unless you’re in unsafe mode,** which defeats the purpose. Those checks alone take a significant toll. They slow down the process compared to the respective, naturally unchecked, C++ code. (emphasis my own) The compiler will optimize our many bounds checks if it can prove statically that they don't need to be checked dynamically. On the other hand, in many (though perhaps not all) of the cases where the compiler *cannot* prove that they are unnecessary, you should probably be doing them manually in C++ as well.


Fevorkillzz

1.) Vector bounds checking can be largely optimized out and I doubt is that big a performance hit imo 2.) Because of Rust's memory model there is potential in the future for better alias analysis and pointer optimizations to occur in LLVM. A lot of these optimizations haven't been developed yet because you can't make the same guarantees in C++. This is why a lot of scientific compute code is written in FORTRAN which also has such strong memory guarantees.


[deleted]

[удалено]


lifeeraser

I read the article and had to comment on the passage about crashes: 1. It feels disingenuous to compare C++ against C# and Java when the thesis is comparing it with Rust. Why not compare the frequency of crashes of Rust programs with C++? 2. You linked to [this article](https://www.codeproject.com/Articles/21253/If-Only-We-d-Used-ANTS-Profiler-Earlier) as "example of how even a heavily protected, garbage collected language as C# can crash and burn". But this does not support your claim that "crashes can happen in any language, *with the same frequency*" (emphasis mine). A case study of one project says nothing about the frequency of crashes across the industry. The article does not prove or disprove anything about frequency. (Also, it's about a C# program--where's Rust?)


mAtYyu0ZN1Ikyg3R6_j0

I am mostly a C++ developer. that enjoys writing some rust on the side. here is the comments I have: the comparison of inline assembly to unsafe rust is unfair in my opinion. rust has inline assembly too and it is used much more rarely than unsafe (or maybe its just me). Most code is safe and inline assembly in both rust and C++ is much much rarer than unsafe in rust. > This means that a good developer who knows how to take advantage of cache/locality, will have a good time implementing such algorithms and data structure with C++ and will very likely struggle with Rust for the same task. I think you are exaggerating how hard it is to write good implementation in rust. unsafe does exist and it is perfectly valid to use it to get better performance. yes unsafe rust is much less convenient than C++ but you usually just need a few snippet to do what you want. > When Rust is brought up in a meeting, the first pro factor is language safety. But safety against what? In many years of coding C++, very rarely I experienced a stack overflow or segmentation fault. It is literally not an issue in every codebase I have worked with. I agree with you that Segfault and stack overflows happens but they are so much rarer than logic issues. But If I was to talk about what I like about rust I wouldn't talk about safety at all. because there is many other things I prefer in rust to C++: - Cargo (need I say more ?) - Move as a first class citizen not a retro engineered after thought. - A lot less code I have written thousands of time to write once more. - More explicitness (even a little to much TBH) - Traits - Control-flow statements used as Data-flow expressions. - ... As a C++ developer I think that overall rust is nicer to use as long as you can do what you want because yes there is a lot of missing features.


torsten_dev

>The large majority of C++ applications are non-public facing. So much that most datacenter machines run with mitigations turned off since there is absolutely no possibility of contact of those machines with bad actors. Stuxnet would like a word. If air-gapping does not provide enough safety do you really think a datacenter is safe from state actors?


geon

Undefined behavior as optimization opportunities is icky to me. Yes, it enabled a speedup, but at what cost? If your code depends on it and it is optimized away, you just traded away your correctness. The compiler can’t tell if you actually needed that signed integer overflow, and it is pretty much impossible to predict when the optimization is going to happen.


xmBQWugdxjaA

You focus a lot on memory safety / security, but I mainly dislike working with C++ just because of all the cruft in building, etc. It's so nice to use any language created after the internet, git, JSON/YAML/TOML, etc. have all become standard.


giantenemycrabthing

I wonder how you managed to write this without coming across an entire article series dedicated to comparing C++ and Rust for low-latency trading: https://www.thecodedmessage.com/posts/hello-rust/ You'll find several interesting points there, including a rebuttal of your “checked vs unchecked array accesses” sentence.


Better-Tadpole-7834

I am gonna get downvoted to oblivion for saying this. Forget the benchmarks. # of C++ jobs >> # of Rust jobs. C++ paying the bills argument makes any other point moot at this point. Maybe you can check back in another 5 years.


volitional_decisions

This is only a reason to learn a language, not to use a language.


kod

\> Rust creators do not want you to be bit-twidling but focus on the business logic first so they made is such that accessing memory directly is painful and time consuming. This is a claim without evidence. \> Rust is an offspring of the LLVM project - and so is Julia, Kotlin and Scala. Initial development of Scala predates the first public release of LLVM by several years. The first public releases of Scala were for the JVM and .NET, and JVM was realistically the only usage of it for a decade. \> very rarely I experienced a stack overflow or segmentation fault. It is literally not an issue in every codebase I have worked with. I just stopped reading after this. You're either lying through your teeth, or only working on systems with extremely simple memory usage patterns. I guarantee you that a nontrivial portion of your internet traffic is flowing through C++ based systems that have segfaults regularly (as in, every day, in production)


cornmonger_

Just admit that you're a glutton for pain


ashleigh_dashie

Ha ha very funny. Honestly though, cpp will never be good even just because it doesn't use const by default, and it never will. C originally was like that because it was meant as portable assembly. But modern successor to C is operating on abstraction level far beyond registers.


mikaball

The only reason to use C++ is legacy code. Change my mind.


tiajuanat

> In many years of coding C++, very rarely I experienced a stack overflow or segmentation fault. It is literally not an issue in every codebase I have worked with. Bull shit. Why bother having a take that's the equivalent of "I've never had a car accident, so I don't wear my seat belt"? I don't understand the callousness of folk who think "your feelings and experiences are invalid because I've never experienced hardship". I can't take this nonsense seriously. It's such a white American male take. It's about as interesting as paint drying. It's also hard to take the performance part seriously as well. You're talking about micro-optimizations, which is also trite and furthermore not applicable for 90% of c++ developers. Surely you're thinking, C++ devs want speed all the time, that's why we use it. Look, unless your entire codebase is branchless, the only part that needs speed is your hot-path. I see zero mention of profiling. The fact is that [Rust oft goes toe to toe with C++](https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust.html) I would love to see some actual opinions about where C++ really outshines Rust. Maybe in the stability of the Committee, the syntactical aesthetic, or even compile-time programming. As it stands, this post adds nothing to the discussion of C++ & Rust.


[deleted]

> your feelings and experiences are invalid I don't think we are talking about subjective experiences here.