On Mon, 25 Mar 2024 at 17:05, Dr. David Alan Gilbert <dave@xxxxxxxxxxx> wrote: > > Isn't one of the aims of the Rust/C++ idea that you can't forget to access > a shared piece of data atomically? If that is an aim, it's a really *bad* one. Really. It very much should never have been an aim, and I hope it wasn't. I think, and hope, that the source of the C++ and Rust bad decisions is cluelessness, not active malice. Take Rust - one big point of Rust is the whole "safe" thing, but it's very much not a straightjacket like Pascal was. There's a "safe" part to Rust, but equally importantly, there's also the "unsafe" part to Rust. The safe part is the one that most programmers are supposed to use. It's the one that allows you to not have to worry too much about things. It's the part that makes it much harder to screw up. But the *unsafe* part is what makes Rust powerful. It's the part that works behind the curtain. It's the part that may be needed to make the safe parts *work*. And yes, an application programmer might never actually need to use it, and in fact in many projects the rule might be that unsafe Rust is simply never even an option - but that doesn't mean that the unsafe parts don't exist. Because those unsafe parts are needed to make it all work in reality. And you should never EVER base your whole design around the "safe" part. Then you get a language that is a straight-jacket. So I'd very strongly argue that the core atomics should be done the "unsafe" way - allow people to specify exactly when they want exactly what access. Allow people to mix and match and have overlapping partial aliases, because if you implement things like locking, you *need* those partially aliasing accesses, and you need to make overlapping atomics where sometimes you access only one part of the field. And yes, that will be unsafe, and it might even be unportable, but it's exactly the kind of thing you need in order to avoid having to use assembly language to do your locking. And by all means, you should relegate that to the "unsafe corner" of the language. And maybe don't talk about the unsafe sharp edges in the first chapter of the book about the language. But you should _start_ the design of your language memory model around the unsafe "raw atomic access operations" model. Then you can use those strictly more powerful operations, and you create an object model *around* it. So you create safe objects like just an atomic counter. In *that* corner of the language, you have the "safe atomics" - they aren't the fundamental implementation, but they are the safe wrappers *around* the more powerful (but unsafe) core. With that "atomic counter" you cannot forget to do atomic accesses, because that safe corner of the world doesn't _have_ anything but the safe atomic accesses for every time you use the object. See? Having the capability to do powerful and maybe unsafe things does not force people to expose and use all that power. You can - and should - wrap the powerful model with safer and simpler interfaces. This isn't something specific to atomics. Not even remotely. This is quite fundamental. You often literally _cannot_ do interesting things using only safe interfaces. You want safe memory allocations - but to actually write the allocator itself, you want to have all those unsafe escape methods - all those raw pointers with arbitrary arithmetic etc. And if you don't have unsafe escapes, you end up doing what so many languages did: the libraries are written in something more powerful like C, because C literally can do things that other languages *cannot* do. Don't let people fool you with talk about Turing machines and similar smoke-and-mirror garbage. It's a bedtime story for first-year CS students. It's not true. Not all languages are created equal. Not all languages can do the same things. If your language doesn't have those unsafe escapes, your language is inherently weaker, and inherently worse for it. Linus