The only concrete example given in the paper (having two contiguous objects and referencing the 2nd one through a pointer on the first one) is frankly contrived and I'm not sure how that can be considered reasonable or good code. The only time I could see myself writing code like that is when dealing with memory-mapped hardware registers at fixed addresses which would let me do funky pointer arithmetic, but in this scenario the pointer is usually tagged as volatile and sometimes accompanied by a memory barrier to make sure the compiler does the right thing.
Note that if the programmers really wanted the code given in the paper to work correctly (i.e. changing y through an offset pointer from x) they could do it by adding a memory barrier such as
asm volatile("": : :"memory");
after the *p = 11;. Is it elegant? Arguably not, but it seems like such a niche use to me that it seems like a fair compromise. It's also not standard C but if you're making such pointer gymnastics chances are you're already making assumptions about your environment that go beyond the standard.I much prefer Rust's approach of "mutable references never alias" and if the programmer really needs to share a reference they have to manually do the work by wrapping. Of course integrating that into C in a backward-compatible fashion doesn't seem very practical.
Rust's approach is certainly easier to understand in some ways, but C is not Rust and you can't just change its semantics without considering the existing body of code.
I looked at C and C++ ownership years ago. I had more limited goals than the Rust borrow checker. I was thinking in terms of permissions - read, write, delete, and keep, combined with reference counting.
Keep permission means you can take a copy of a pointer and export it to a larger scope. Like passing in a pointer to an object to be added to a collection. The stored pointer will outlive the return from the function that added it to the collection. It "kept" it.
If you pass a reference to an object without keep permission, the called function can't keep it. So the language does not have to increment the reference count. In fact, you don't even have to pass the reference count, just the raw object pointer. So objects passed by non-keep reference to a function incur no extra overhead. That's a very common case. "Keeping" is relatively rare.
So you get reference count allocation without much overhead.
Workable, but a dead end politically in C. Rust essentially makes all references "non-keep", unless you explicitly use reference counts.
This rules out a vast majority of fundamental algorithms. Jumping through hoops for getting simple logic working doesn't sound very desirable.
>This rules out a vast majority of fundamental algorithms.
To be more precise, in Rust:
- '&mut T' (a mutable reference to T) can never alias.
- '(asterix) mut T' (a mutable pointer to T) can alias.
The advantage is that you can write most production code with a mix of '&T' and '&mut T', which allows the compiler to assume no mutable aliases anywhere. But if you're building a doubly-linked list, then you can explicitly choose '(asterix) mut T'. (For other options, see https://rust-unofficial.github.io/too-many-lists/.)
In practice, it depends a lot on the code you're writing. I've written tens of thousands of lines of production Rust without needing shared mutability. But if I were writing a traditional game or GUI toolkit, then I might miss shared mutability a lot more.
How does the rust compiler know if two references passed in from calling code can alias or not? (I.e., what if I get the type annotations wrong?).
In general, determining if two pointers can alias is equivalent to the halting problem.
The default C way is “assume aliasing unless you can prove otherwise”. This seems safer and like less work to me.
(The C default you refer to is at the compiler internals level. The Rust alias prevention we’re talking about is at the language level. C compilers will try to avoid generating code that will behave incorrectly in the presence of aliasing, but do nothing to prevent aliasing and the possible logic and thread safety issues caused by aliasing. Rust makes almost all of these cases disappear for the programmer.)
But I'm not proposing to bring that to C (although it seems that generics are slowly creeping in by way of the macro system), I'm just saying that I think the changes proposed in this paper would make the language more complex without really making it easier to write C code 99% of the time.
Aliasing pointers ruing performance of array/pointer access optimizations. https://en.wikipedia.org/wiki/Restrict
The example in the paper linked is specifically a situation where the compiler does not normally catch the aliasing between two pointers which is the opposite of the problem "restrict" solves (i.e. a situation where the compiler emits code that assumes that two pointers can alias when in fact they do not).
Here’s the direct link to the pdf: https://hal.inria.fr/hal-02089889/document
Please tell that to the many millions of C/C++ programmers writing new code every day, who are only vaguely familiar with Rust if at all. If by consensus you mean "consensus amongst the vanguard of Hacker News" then yes. If you mean "consensus" as in the "consensus of the software development industry" then absolutely not.
Just call it "C20", and they will learn it in a week.
Also, Perl does not exist and is actually an urban legend from the '90s.
There is room for a new systems programming language but the current solution to the above issues is to make the code "unsafe", slower, and/or more bloated, which offsets its nominal benefits. For many C++ software architectures, memory safety is a non-issue in practice, so that isn't always a compelling feature. The benefits of Rust versus C++ are significantly overstated by its fans, and its current limitations tend to be overlooked. (I can't speak for C, it has been too long since I worked with it at scale.)
This is fairly magical when you use it but it requires a different coding style than some systems programmers are used to. Instead of writing code, you end up writing a lot of meta-code that generates and verifies what actually gets compiled. Recent versions of C++ have invested a lot in making these facilities expressive and reasonable to use in complex software. In my experience this has substantially reduced the code defect rate and simplified testing. Once you've written a system this way, it is very hard to go back.
Not having these facilities would be a huge step backward for writing practical high-assurance/high-reliability code. AFAICT, Rust is still quite primitive in this regard.
Rust does have a procedural macro system which can be used to do these things. Surely actual procedural macros are better than hacking the template system to generate custom code, which is the typical approach in C++.
(Sorry, I know this is a few days old by now...)
Due to the type system, safe Rust code has much less need for sanitizers (leaks and lock order checks are useful) or fuzzers (they'll only find infinite loops); granted, you do have a point in that sanitizers are very useful for unsafe Rust code.
There's AFL for Rust: https://github.com/rust-fuzz/afl.rs
[Edit:] With regard to theorem provers, my understanding is that you typically work in some sort of functional language and the correct C/C++ code is generated as a final step at the end (program extraction); certainly Coq and the HOL family work that way.
There's already Rust program extraction for Coq, although I wouldn't be surprised if it's not as mature as C yet: https://github.com/pirapira/coq2rust
In other areas, Rust already has an advantage; compare Cargo with the Babylonian chaos of C++ build sytems, where the question isn't even which one you want to use, but how many different ones are imposed on you by external constraints and how to integrate them without going crazy.
Provers are also a fair complaint, though it’s going to be comparatively super rare that that is an issue in practice :p
While C's copy-paste compatibility is C++ security story Achilles heel, there is still quite a lot that can be used for writing safer code.
https://herbsutter.com/2018/09/20/lifetime-profile-v1-0-post...
Yet, this is good to see C++ trying to catch-up. (And if it's catching up, it means Rust is ahead in terms of static analysis ;)
The difference is that C++ makes it convenient to write unsafe code, but that it also makes it possible to write safe systems code at a much higher level of abstraction than any other language I’ve heard of (templates are Turing complete, and modern tooling makes them reasonable to work with).
So, the question is, what does Rust make C++ developers give up that can’t be achieved via C++ style guidelines?
Also, 10 years from now, when the state of the art of systems programming advances, we’ll be able to backport the ideas C++ (just like we do today).
Does Rust have a track record of supporting that?
I'm really skeptical of backporting Rust's wins to C++. The concept of mutability cuts across the language and I don't see how you do it without bifrucating an already complicated language.
How hard would it really be for someone to write a static analyzer for C++ that ensures there is only one non-const reference/pointer to an object active at any given time?
Throw in an object lifetime tracker for allocs and you'd have pretty safe C++ without having to rewrite a billion line of code.
At work we use a pretty big C++ library maintained by absolutely brilliant people, and there still are memory issues and the occasional segfault. It gets tiresome. I'm not convinced anyone can write really safe C++, whereas outside of `unsafe` it's quite easy to write safe rust.
[citation needed]?
I could definitely believe there are such things, but all the examples I've encountered are things like doubly-linked lists where the complainer flatly ignores the existence of the 'unsafe' keyword.
But every sane safety advocate also acknowledges that we aren't going to throw away the millions of lines written in C and C++ tomorrow, and there needs to be a way to keep UNIX derived platforms, while improving their security.