Rust-like features in C++

I just read a post about some fancy template features applied to C++ under a very recent version. Since my C++ is quite Rusty in a different way, I thought I’d point it out to the C++ experts here and ask what you think about: https://a10nw01f.github.io/post/advanced_compile_time_validation/

3 Likes

The problem with this is, it is probably extremely expensive to do this at compile time, both disk space and time wise for C++ since this is using template meta programming… it could be useful once a form of it is ratified into a standard which honestly some kind of safe C++ seems like it should be on the roadmap for the working group.

The real roadblocker though its using non standard features so even next versions of the same compiler may not work the same.

Some additional thoughts/strategies on the topic, safe C++, by the developer of the Vale language:
https://verdagon.dev/blog/vale-memory-safe-cpp

1 Like

Memory safety in C++ comes from things like containers, smart pointers and RAII. Pretty sure that makes the language safer than Rust, without making the programmer jump through hoops to get work done.

Pretty sure that makes the language safer than Rust

It doesn’t.

2 Likes

That is definitely false. Because of human error in implementing those patterns. C++ doesn’t do anything to ensure are you in fact being safe.

Containers aren’t a pattern, they’re a library feature.
Smart pointers aren’t a pattern, they’re a library feature.

If you mean they can have bugs, so can the Rust compiler.

Pointless distinction … smart pointers still require you to follow the correct usage pattern or they don’t work at all, and nothing forces you to.

The same applies to containers which have been leaking memory since they were first introduced because of mistakes in implementation… that exist in real code in the wild.

Nothing about these features ensures memory safety unlike a borrow checker like rust uses or other method of ensuring memory safety. Basically Rust is doing the opposite of C++ …and enforcing safety by default, while allowing you to opt out via unsafe keyword…which is still better than C++ because it invites scrutiny of unsafe code.

You really like the idea of forcing people to do things.

1 Like

You are being weird. Forcing people to not write memory leaks, after decades of failing to otherwise, is a good thing.

1 Like

There’s no way to leak memory by using std::getline for example. That’s kind of the point.

Too bad that isn’t true. std:getline can leak…and you’d know that if took 5 sec to google search before posting. Not to mention C++'s getline usually being atrociously slow.

If we’re going to use trivia googled in five seconds as arguments, I might as well point out this recent article that details how most software vulnerabilities found last year are the kind Rust doesn’t protect against. But I don’t want to bash Rust. My point is that trying to turn C++ into Rust doesn’t strike me as the right way to make it safer, insofar as it needs more safety. If people want to use Rust, it’s right there already. And if I’m not mistaken, it already runs on Haiku.

You could take a look at CPPFront.
It’s an interesting update to C++ and compiles to C++ code.

1 Like

The debate about memory safety dates all the way back to the 1960s and the use of garbage collection in LISP.

Read this again: this is a solved problem since the 1960s. Programmers should not have to think about memory allocation anymore for the last 60 years. That’s even before C and UNIX were invented. But no, instead we chose C and C++, where the memory management is explicit and the developer must take care of allocating and releasing memory. Sure, there are some helper classes in C++, but it still is something you need to think about and take into account when designing software?

Why is that so? In the 1960s, the reason was performance: by having control on memory allocation, you can, in therory, be a lot more efficient about it. For example, you can use object pools, either to group allocations of objects of the same size together and reduce memory fragmentation, or to create a single large memory allocation, do all your temporary work inside it, and then free it all at once when done.

How often do C and C++ programmers actually do that? Practically never. It’s just malloc/free or new/delete, a very generic method that’s not particularly well suited to anything. There are exceptions, of course: in Haiku’s kernel and some of the userspace kits, we do need to handle memory allocations in such specific ways. That would also be the case in high performance computing, or in videogames. But for the typical desktop applications that just use new/delete or malloc/free? It seems unfortunate to pick a language that allows you so much control about memory management, at the cost of having to do it manually. And then do it manually in the least efficient way possible: by using a single generic non-specialized allocator that also requires you to manually track the ownership of every object (yes, even with smart pointers you can very easily end up with dangling references, you can have circular references that prevent objects being deleted, and so on).

Why pretend that this is a better solution?

1 Like

It’s just malloc/free or new/delete, a very generic method that’s not particularly well suited to anything.

Calling new/delete manually still gives you more predictable performance compared to garbage collection.

Why pretend that this is a better solution?

Because alternative solutions do not work that well, simple as.

First of all, I much prefer to use a garbage-collected language when it’s an option. But sometimes it isn’t, or else it’s a very inefficient option. Performance still matters. In those cases I’ll take a language that helps me at least somewhat.

Second, explicit use of new and delete in modern C++ is kind of iffy. You probably want std::make_unique instead. Sadly there are a ton of ancient codebases out there, and equally many tutorials that teach C++ as if we were in 1994. In fact I wrote a language tour of my own just recently as a reaction to this issue.

Third, even smart pointers (with their well-known limitations) are mostly just needed to support polymorphism, because in C++ that’s a runtime feature. Nim for example has compile-time polymorphism, thus making the issue moot. Now that would be a feature to emulate! Most of the time, you can simply use generic containers, that are efficient and reasonably safe. I mean, of course you can break them if you put your mind to it. It’s true of everything.

Is there room for improvement? Certainly. But in C++, improvement is at least possible. C? Now that’s a language I’m afraid to touch. And again, when a garbage-collected language is an option, I’ll gladly take it instead.

(But don’t be fooled! Vala pretends to be, but it simply uses the reference counting scheme in GLib2, and it’s a lot easier to make a Vala program crash than a C++ one, even though Vala has nullable types. Go figure. Safety as a buzzword.)

1 Like

I tried a home rolled region allocation system for a Haiku project. And gave up on it. I’m not really much of a fan of C++, though, in general.

Rust reminds me a lot of ML. Ocaml 4.14 works pretty well on Haiku, and it’s a hell of a lot easier to use.

Side channel attacks which you seem to reference are mostly a hardware problem with software mitigations… they aren’t something you can fix in software ahead of time in practice and its always better to fix the hardware not to have these issues then remove the mitigations.

garbage collectors aren’t inherently leak free either, have numerous issues as well with latency and overhead.

Rust is very plainly moving as much of the reasoning about memory safety into compile time guarantees… that is all it is, so you get most of the performance of C++, with non of the memory safety bugs. It’s also going to inherently eliminate all the issues you’d have with a GC language as well… because it isn’t garbage collecting. It’s also missing the overly complicated objects of C++ because, those end up just causing abstraction hell.

The myriad of ways C++ can kind of do similar things is actually a negative… because none to almost none of them do it at compile time in a standardized way that people can readily adopt and be confident they are doing it correctly.

Edit: I’ll add that if a standard borrow checker feature could be added to C++ with an opt in “safe mode” so you could gradually migrate code to it I think that would also be quite useful.

2 Likes