Also, the container and mutex tricks you mention sound interesting, but I don’t see why they can’t also be used in C++ (which has a turing complete type system / checker).
Also, the container and mutex tricks you mention sound interesting, but I don’t see why they can’t also be used in C++ (which has a turing complete type system / checker).
It doesn't explain how it would statically ensure that a moved-from unique_ptr (or equivalent) can not be used. In fact the only mentions of moves are that owning pointers can only be moved and soft pointers can be moved or copied, but C++'s move does not remove any access, it just moves the content leaving the moved-from object in a "valid but unspecified state".
Note that valid != safe. Dereferencing a moved-from unique_ptr is unsafe for instance.
Rust's affine types solve this issue, a moved-from type (Box included) simply can't be used, its scope ends when it's moved.
> Looking online, I see that people implement all the same primitives in rust, with exactly the same safety caveats.
Rust's (safe) pointers and references don't throw exceptions on explicit use after free because such code doesn't compile at all, and its equivalent to dynamic_cast has to be very specifically opted in: https://doc.rust-lang.org/1.19.0/std/any/trait.Any.html#meth...
Which essentially goes at the cost of having Java-style semantic memory leaks (very generally, _any_ kind of keeping-an-object-as-long-as-at-least-one-reference-exists suffers from it) => we still have to pick our poison (personally, I _strongly_ prefer to avoid refcounting, and it does work like a charm in a few very serious million-LoC/billions-transactions projects, but I do agree that opinions may differ).
Rust references work the opposite way. References don't extend the lifetime of their source, and a reference outliving its referent is a compile-time error.
> we still have to pick our poison (personally, I _strongly_ prefer to avoid refcounting, and it does work like a charm in a few very serious million-LoC/billions-transactions projects, but I do agree that opinions may differ).
I have no idea what the hell you're talking about, but you seem to suffer from pretty significant misunderstandings.
P.S. FWIW, Rust's references ~= OP's "naked pointers" (NOT 'soft pointers'), and SaferCPP's 'scoped pointers'. A useful tool, but is not sufficient in quite a few real-world use cases.
So it's understood that a memory safe language will generally be composed of syntactic and semantic features which help at compile time, and runtime checks to close any remaining holes. If some particular implementation happens to have a few more things in the latter category compared than is usual, that doesn't prevent it from being "memory safe", it just makes it more awkward to use and prone to bugs.
In principle, the same distinction applies even to type safety.
Indeed, array manipulation is completely unsafe in most languages, as array indices are effectively unityped. (This may be a unitype of everything, as in Python, or a unitype of indices for all arrays, as in Java or ML.) There are several ways to fix this issue, with various tradeoffs between convenience and expressive power. None of them has become mainstream, but it's good to remember that they do exist.
> So it's understood that a memory safe language will generally be composed of syntactic and semantic features which help at compile time, and runtime checks to close any remaining holes.
Safety is any means by which you can establish that every operation a program may perform is meaningful. Now, I don't know about you, but at least to me, it is never meaningful to dereference an invalid pointer or use an invalid array index. Whether the error is trapped at runtime is neither here nor there.