What actually is this circle-lang site, and who runs it? The main page seems to just redirect to example.com, and I don't recognize the name of the author.
I'm surprised you've not heard of the author (Sean Baxter). He's pretty well known among people who are interested in c++ standards proposals. He single handedly wrote his own C++ front-end and then proceeded to implement a butt load of extensions which other members of the committees poo-pood as being to hard to implement in the compiler. Every couple of weeks he implements something new. He's a real joy to follow.
Why Safety Profiles Failed
211–220 of 233 posts
Re: Why Safety Profiles Failed
#212Earlier quoted context omitted.
> "No mutable aliases" is a mistake; it prevents many useful programs. Does it? You didn't list any. It certainly prevents writing a tremendous number of programs which are nonsense.
OP mentioned std::sort and the rest of std::algorithm as useful functions that use mutable aliasing.
Re: Why Safety Profiles Failed
#213Earlier quoted context omitted.
You cannot cause undefined behavior with integer overflow using + in Rust. That behavior is considered an error, but is well defined.
If it requires the programmer to bear the responsibility for proper usage (eg. must use checked_add not rely on panic), how's that different than the issues with undefined behavior? I'm also concerned with the differing functional behavior between debug and release, and the mistaken impression it could create (eg. code handles overflow in debug fine due to panic but blows up on release as the proper solution is not u…
It comes down to the blast radius for a mistake. A mistake involving UB can potentially result in completely arbitrary behavior. A mistake in the safe subset of a language is still a mistake, but the universe of possible consequences is smaller. How much smaller depends on the language in question.
> I'm also concerned with the differing functional behavior between debug and release
IIRC this was a compromise. In an ideal world Rust would always panic on overflow, but the performance consequences were considered to be severe enough to potentially hinder adoption. In addition, overflow checking was not considered memory safety-critical as mandatory bounds checks in safe code would prevent overflow errors from causing memory safety issues in safe Rust.
I believe at the time it was stated that if the cost of overflow checking ever got low enough checking may be (re)enabled on release builds. I'm not sure whether that's still in the cards.
It's not ideal and can lead to problems as you point out when unsafe code is involved (also e.g., CVE-2018-1000810 [0]), but that's the nature of compromises, for better or worse.
[0]: https://groups.google.com/g/rustlang-security-announcements/...
Re: Why Safety Profiles Failed
#214Earlier quoted context omitted.
You cannot cause undefined behavior with integer overflow using + in Rust. That behavior is considered an error, but is well defined.
If it requires the programmer to bear the responsibility for proper usage (eg. must use checked_add not rely on panic), how's that different than the issues with undefined behavior? I'm also concerned with the differing functional behavior between debug and release, and the mistaken impression it could create (eg. code handles overflow in debug fine due to panic but blows up on release as the proper solution is not u…
Re: Why Safety Profiles Failed
#215Earlier quoted context omitted.
> There are reasonably good c++11 conventions for lifetimes [...] Almost nobody follows them [...] I swear I'm not trying to be snarky or rude here, but is it actually a "convention" if almost nobody follows it? This seems like one example of my general issue with C++, in that it could be great if everyone agreed to a restricted subset, but of course nobody can coordinate such agreement and it doesn't exist outside c…
What we have is a human problem. The convention exists in enough places (though in slightly different forms) to call it a convention, but it needs more adoption. Every once in a while someone who writes a lot of Rust will blog about some code they discovered that was 'unsafe' and after looking close they realized it wasn't doing something that fundamentally required unsafe (and often fixing the code to be safe fixed…
https://devblogs.microsoft.com/oldnewthing/20241023-00/?p=11...
Notice the use of C's memcpy() function.
This is exactly the kind of posts where showing best practises would be quite helpful, as education.
Re: Why Safety Profiles Failed
#216Earlier quoted context omitted.
> There are reasonably good c++11 conventions for lifetimes [...] Almost nobody follows them [...] I swear I'm not trying to be snarky or rude here, but is it actually a "convention" if almost nobody follows it? This seems like one example of my general issue with C++, in that it could be great if everyone agreed to a restricted subset, but of course nobody can coordinate such agreement and it doesn't exist outside c…
Honestly, I blame MSVC for a lot of lost momentum when adopting new standards, given it takes them more than 4 years implementing those features. Ofc, this isn't the case for C++11 today, but a lot of projects were started prior to 2015. And don't get me started on C itself. Jesus Christ.
They are the only C++ compiler that properly supports all C++20 modules use cases, while clang still doesn't do Parallel STL from C++17, for example.
They support C17 nowadays, where many embedded folks are slowly adopting C99.
And the UNIX story outside clang and GCC is quite lame, most still stuck in C++14, catching up to C++17.
Likewise, consoles, C++17.
Re: Why Safety Profiles Failed
#217Earlier quoted context omitted.
OP mentioned std::sort and the rest of std::algorithm as useful functions that use mutable aliasing.
And other languages implement the same thing just as fast without mutable aliasing.
There is movement towards adopting the same approach for C++ by some vendors. The biggest problem is that there's a lot of crap C++ out there buried in erroneous custom comparators and so if you change how sorting works even very slightly you blow up lots of fragile nonsense written in C++ and from their point of view you broke their software even though what happened is they wrote nonsense. Next biggest problem is that C++ named their unstable sort "sort" so sometimes people needed a stable sort and they got an unstable sort but in their test suites everything checked out by chance, and with a new algorithm now the test blows up because it's an unstable sort...
Re: Why Safety Profiles Failed
#218Additionally I still cannot understand why they didn't make iterators safe from the very beginning. In the alias examples some iterators must alias and some not. With safe iterators the checks would be trivial, as just the base pointers need to be compared. This could be done even at compile-time, when all iterators bases are known at compile-time. Their argument then was that iterators are just simple pointers, not…
The C++ committee and standard library folks are in a hard spot. They have two goals: 1. Make primitives in the language as safe as they can. 2. Be as fast as corresponding completely unsafe C code. These goals are obviously in opposition. Sometimes, if you're lucky, you can improve safety completely at compile time and after the safety is proven, the compiler eliminates everything with no overhead. But often you can…
That's why C++ 11 move is not very good. The safe "destructive" move you see in Rust wasn't some novelty that had never been imagined previously, it isn't slower, or more complicated, it's exactly what programmers wanted at the time, however C++ could not deliver it compatibly so they got the C++ 11 move (which is more expensive and leaves a trail of empty husk objects behind) instead.
You're correct that the big issue is culture. Rust's safety culture is why Rust is safe, Rust's safety technology merely† enables that culture to thrive and produce software with excellent performance. The "Safe C++" proposal would grant C++ the same technology but cannot gift it the same culture.
However, I think in many and perhaps even most cases you're wrong to think C++ is preferring better performance over safety, instead, the committee has learned to associate unsafe outcomes with performance and has falsely concluded that unsafe outcomes somehow enable or engender performance when that's often not so. The ISO documents do not specify a faster language, they specify a less safe language and they just hope that's faster.
In practice this has a perverse effect. Knowing the language is so unsafe, programmers write paranoid software in an attempt to handle the many risks haunting them. So you will find some Rust code which has six run-time checks to deliver safety - from safe library code, and then the comparable C++ code has fourteen run-time checks written by the coder, but they missed two, so it's still unsafe but it's also slower.
I read a piece of Rust documentation for an unsafe method defined on the integers the other day which stuck with me for these conversations. The documentation points out that instead of laboriously checking if you're in a case where the unsafe code would be correct but faster, and if so calling the unsafe function, you can just call the safe function - which already does that for you.
† It's very impressive technology, but I say "merely" here only to emphasise that the technology is worth nothing without the culture. The technology has no problem with me labelling unsafe things (functions, traits, attributes now) as safe, it's just a label, the choice to ensure they're labelled unsafe is cultural.
Re: Why Safety Profiles Failed
#219Earlier quoted context omitted.
Rust is unique in being both safe and nearly as fast as idiomatic C/C++. This is a key differentiator between Rust and languages that rely on obligate GC or obligate reference counting for safety, including Golang, Swift, Ocaml etc.
it's not clear to me that GC is actually slower than manual memory management (as long as you allow for immutable objects. allocation/free is slow so most high performance programs don't have useless critical path allocations anyway.
However, GC loses determinism, so if you have non-memory resources where determinism matters you need the same mechanism anyway, and something like a "defer" statement is a poor substitute for the deterministic destruction in languages which have that.
Determinism can be much more important than peak performance for some problems. When you see people crowing about "lock free" or even "wait free" algorithms, the peak performance on these algorithms is often terrible, but that's not why we want them. They are deterministic, which means we can say definite things about what will happen and not just hand wave.
Re: Why Safety Profiles Failed
#220Earlier quoted context omitted.
If the vector has to be resized in push_back, the implementation copy-constructs a new object from x. It resizes the buffer. Then it move-constructs the copy into the buffer. The cost is only a move construct per resize... So it's on the order of log2(capacity). Very efficient. But don't get used to it, because there are plenty of other C++ libraries that will break under aliasing.
I think the implementation in C++ does not do realloc on push_back, but allocates new memory, creates the new element and only then deallocates it. I believe the reason f3 is safe is that the standard says that the iterators/references are invalidated after push_back – so push_back needs to be carefully written to accept aliasing pointers. I am pretty sure if I were writing my own push_back it would do something like…
The difference is what these APIs do when we're making small incremental growth choices, Vec::reserve_exact and the std::vector reserve both grow to the exact size asked for, so if we do this for each entry inserted eight times we grow 1, 2, 3, 4, 5, 6, 7, 8 -- we're always paying to grow.
However when Vec::reserve needs to grow it either doubles, or grows to the exact size if bigger than a double. So for the same pattern we grow 1, 2, 4, no growth, 8, no growth, no growth, no growth.
There's no way to fix C++ std::vector without providing a new API, it's just a design goof in this otherwise very normal growable array type. You can somewhat hack around it, but in practice people will just advise you to never bother using reserve except once up front in C++, whereas you will get a performance win in Rust by using reserve to reserve space.