Comment by jandrewrogers

6 months ago

Unfortunately, Rust is significantly less expressive than C++ and therefore is unlikely to replace it for high-performance systems code. As much as I don’t like C++, it is very powerful as a tool. The ability to express difficult low-level systems constructs and optimizations concisely and safely in the language are its killer feature. Once you know how to use it, other languages feel hobbled.

C++ doesn't allow you to express low level systems constructs concisely and safely though. You usually get neither.

Look at the first example in the article, where the increment can overflow and cause UB despite that overflow having completely defined semantics at the hardware level. Fixing it requires either a custom addition function or C++26, another include, and add_sat(). I wouldn't consider either concise in a program that doesn't include all of std.

  • This assumes you are writing C++ in the most naive way possible. I’m sure some people do that but nothing requires it. The capabilities of a language are not defined by its worst programmers.

    Modern C++ allows you to swap out most features and behaviors of the language with your own implementations that make different guarantees. C++ is commonly used in high-assurance environments with extremely high performance requirements, and it remains the most effective language for these purposes because you can completely replace most of the language with something that makes the safety guarantees you require. This is rather important. For example, userspace DMA is idiomatic in e.g. high-performance databases kernels; handling this is much safer in C++ than Rust. In C++, you can trivially write elegant primitives that completely hide the unusual safety model. In Rust, you have to write a lot of ugly unsafe code to make this work at all because userspace DMA isn’t compatible with a borrow checker. There can always be multiple mutable references to memory but it is not knowable at compile-time, safety of an operation can only be arbitrated at runtime.

    Of course, it is still incumbent on the developer to use the language competently in all cases.

    •     The capabilities of a language are not defined by its worst programmers.
      

      Is the implication here that Bjarne is a bad C++ developer? If the person in charge of the EWG fails "to use the language competently in all cases", what hope is there for the rest of us mere mortals?

      For what it's worth, unsafe Rust is safer than C++. There's very little UB to explode your carefully crafted implementations. Safe rust of course has no UB except for what you write in unsafe blocks, so it's safer still and there's no real difference in the abstractions you can write with concepts vs traits.

      I'm not actually arguing for rust here though, because this isn't a great showing for it. Trying to write the related add_wrap(T, T) function in rust is stupidly verbose compared to add_sat(T, T) thanks to bad decisions the num_traits authors made. What I am saying is C++ isn't a form of high level assembly like your original comment suggested. Understanding the relationship between the language and the hardware takes a lot of experience that most people don't use when writing code.

      2 replies →

    • DMA being a problem appears to be mostly a problem with a lack of identification of the data. If the shape of the data could be verified by the language runtime, instead of being an arbitrary stream of bytes whose meaning must be known by the recipient without any negotiation, this form of unsafety would disappear, since the receiving code simply needs to assert the schema, which could be as simple as checking a 32 bit integer.

      Then all you need to do is also verify that the sending code adheres to the schema it specified.

      This has very little to do with borrow checking. From the perspective of the borrow checker, a DMA call is no different from RPC or writing to a very wide pointer.

Most high performance code is vectorized and Rust is better at autovectorization and aliasing analysis than C++, so I'm not really seeing your point.

Having to drop down to intrinsics early is not a strength.

  • hm, I'd be concerned about relying on autovectorization. How much better is 'better'? Compiler friends have told me that something permute-heavy like sorting is unlikely to soon work, if ever.

    My biased opinion, from doing this full-time in C++, is that the C++ SIMD story is much further along, especially regarding mature libraries.