> One of my favourite examples is volatile.
It's strange that an ancient feature that is almost practically deprecated serves as one of your favourite examples.
I'm not an expert, but volatile was originally meant to access hardware registers and I believe it did fulfill its purpose at the time. I've never reasearched the official definition of volatile, but it is widely acknowledged that there are few or even no legit uses of it today, because it is not suited to general concurrent programming. This seems like a good SO answer: https://stackoverflow.com/questions/2484980/why-is-volatile-...
If I understand right, I think you could do multithreaded programming if you restrict to doing message passing of message structures that are marked all volatile. But you can't use much else, because non-volatile data accesses can be reordered around volatile data accesses.
In short, probably just don't use volatile. Use the "modern" memory barriers, those exist in C/C++ just as they exist in Rust.
> Would it mean anything to, for example, integer divide an MMIO fetch by fifteen and write it back? a/= 15; No. So then why make it a type qualifier?
This seems a strange example to me as well. This is equivalent to (a = a / 15). I.e. a volatile read followed by a volatile write. Not defending volatile at all, but I can't see what is so wrong here.
On a different note, attaching too many semantics to types is basically the complaint that I have about most languages. Types are too static and too inexpressive to capture all but the most trivial invariants, so complicated types often require messy and theoretically unnecessary workarounds. I always say, I'd almost like to types capture only the physical shape (sizes, alignment...) of the data, because that is extremely important for optimization and also it doesn't really change. In C, that is almost true, with some exceptions (signed/unsigned, and yes, the crufty volatile)