There seems to be some asm code as well, which obviously does not enjoy the safety advantages of Rust.
But "safe" rust at least intrinsically protects from use-after-free, double free, dangling pointers, null-pointer dereferences, out-of-bounds accesses, … You may still have logic bugs of course (though the richer type system expressivity also allows better static encoding of application & domain logic), but these baseline memory-safety issues will only be a problem in specific and tagged `unsafe` blocks rather than throughout the application.
As well as introduce new ones.
Even though I am a believer I would love to see some solid evidence of Rust making code safer.
If, on the other hand, you rewrite the code line by line from C to Rust (which Rust is actually quite amenable to!), faithfully translating the semantics of the C code into the Rust code (and thereby having to use lots of unsafe{} blocks) then you can avoid most of the problems—what you'll end up with will essentially be the same as what a hypothetical C-to-Rust transpiler would output.
Importantly, you can also translate the test suite in this same way.
After that step is done, you can just refactor the resulting code, replacing unsafe{} blocks with Rust idioms, and then rerunning the tests (which you can leave un-idiomatized) as regression tests.
Ah, and not passing it around by address or reference, instead of actually moving ownership.
C++17 is a great improvement, but for it to work out in this context, developers need to actually write C++ instead of "C with C++ compiler".
- std::move out of a unique_ptr x and "*x" is a null pointer dereference,
- take a reference into a unique_ptr, and it becomes dangling if it is held after the pointer is deallocated,
- an T[N] array doesn't get bounds-checked whether or not it is stored in a std::unique_ptr (it is nice that it reduces the number of raw pointers flying around, which likely does reduce the number of out-of-bounds accesses, but it doesn't solve them)
If you want to pay the cost of runtime array checking every time, an impl in operator[] is just a few lines long. Thankfully it is becoming more common to run programs under ASAN in dev mode.
#include <memory>
int main() {
std::unique_ptr<int> p = std::make_unique<int>(0);
std::unique_ptr<int> q(std::move(p));
return *p;
}
Maybe you mean it doesn't happen much in practice, which might be true (although, we need to be comparing use-after-move with use-after-free etc., which also don't happen that much, per line-of-code), but is a different point. The fact remains that unique_ptr doesn't solve use-after-move.> If you want to pay the cost of runtime array checking every time, an impl in operator[] is just a few lines long
This is also a different point, and moving the goal posts.
In any case, which operator[] exactly? AFAIK, neither T[N] nor T* can have a custom operator[] (and getting the bounds of a raw pointer is essentially impossible), and none of the operator[]s of std::array nor std::vector nor std::span do bounds checking (sure, you can use ...::at for the first two, but you have to remember to do that everywhere, going against the default that pretty much every programming language uses).
> Thankfully it is becoming more common to run programs under ASAN in dev mode.
Yes, this is great! However, this is yet another different point, and it is orthogonal to modern C++ and its fancy new features like std::unique_ptr: C++98 code, and even C code benefit from ASan.
std::unique_ptr<int> foo(new int(10));
void bar(int& x);
int main() {
bar(*foo);
return 0;
}
void bar(int& x) {
foo = std::unique_ptr<int>(new int(20));
std::cout << x; // use after free
}
No warnings on any warning level.The list you were responding to didn't include memory leaks.
In those languages you have "Logic errors" to debug, in C and its direct descendants you have "Logic errors" + "UB errors" + "Memory corruption errors" to debug.
Also having the use of unsafe explicit in the type system, means that it is relatively easy to track down where such issues might occur, whereas in C every line of code can be a source of problems, depending on which compiler and flags are being used.
Again, this applies to all memory safe system programming language, not just Rust.
EDIT: Fixed the NEWP reference, as ESPOL came first and NEWP only replaced it in 1976.
Yes, but now we're discussing only those that practically matter to more than 5 people today.
Plus the point was to talk about safe systems programming languages in general.
Or should we ignore history just because UNIX won on the server?
https://en.wikipedia.org/wiki/History_of_Unix
https://en.wikipedia.org/wiki/Unix
Of course we can play semantic games about the use of UNIX word, and GNU/Linux not sharing any code from either BSD or AT&T linage, it doesn't make it any less UNIX.
Had Linux not copied UNIX, and provided a playground for UNIX vendors kind of outsource their development costs, it would have turned out to yet another hobby OS.
What Linux (and NT POSIX to certain degree) allowed, was to move the existing investment in software from hodge-podge of mutually incompatible, but expensive UNIX systems, to somewhere else.
Starting by wanting to copy Minix, an educational OS that copied UNIX, getting the GNU tools on board (which goal was to copy UNIX), kernel architecture, device drivers subsystems, userland, culture, ....
My old stack of Linux Journal editions at home are pretty clear what the ongoing story was.
If NT was a copy of anything, it was VMS given Dave Cutler's contribution to its design.
< barrbrain> atomnuker: I felt good when my patch was done and I had exactly what I set out to write.
< barrbrain> I didn't enjoy the compiler forcing me to fix all my bugs upfront.
There are also advantages other than safety to using a modern systems language that has learned something from the last 30 years of programming language design but doesn't come with all of the baggage of C++.
Bugs don't magically "shift". If you eliminate a class of bugs, it's gone (e.g. memory bugs).
Whether you can still have other bugs (e.g. logic bugs) that's irrelevant, you could still have those in C as well.
I don't think it's particular likely, but there are always trade-offs.
It depends... You can’t have dangling pointers in Java - instead, you can “leak” memory by holding onto your objects even when they’re not needed anymore.
You are implying that not being able to have dangling pointers in Java (and thus eliminating an entire category of bugs) inevitably leads to the new category of bugs of leaking memory by holding on to references. But that is the case in any language that allows dynamic memory allocation. You can leak memory in Rust, C++ and VB if you don't dealloc.
The switch reduces bugs some, and definitely reduces the severity, but I would largely put the bugs in the same category and say it hasn't been eliminated.