I think from a PLT perspective, the interesting question is 'can the problems Rust is supposed to solve be resolved in a simpler (but radically different) conceptual model'? We started off originally with a spatial conceptual model -- stack, heap, scope -- that mapped lifecycle semantics to spaces and transitions between spaces. Concurrent programming stressed out that model and we're here with explicit mapping of semantics by the programmer. I've been wondering lately whether a new spatial approach can yet again simplify matters.
I've been closely following the development of Vale since I first saw it here. Though their approach is slightly higher-level than Rust and requires (some) runtime safety checks (though to be fair, so does GC).
https://verdagon.dev/blog/zero-cost-memory-safety-regions-ov...
I think it would be tough to change the spatial model in a language as low-level as Rust, because that spatial model is just reflecting how your CPU actually works under the hood. If you try to hide that away, the programmer is going to end up losing some control.
For a while I was thinking of the question of "Why didn't production rules (a.k.a. expert systems, business rules engines) catch on as a general approach to programming?" Systems like that can do some remarkable things, similar to Excel and logic programming in that you can write programs that aren't concerned with the exact sequence of events that things have to be done in. This is not only convenient for the non-professional programmer, it makes some "chicken-and-egg" problems in conventional programming languages trivial, back in the 1980s people would have seen this to answer to parallel programming, today we might see it as an answer to the async choreography hell of front end programming.
The thing is that sometimes the order of events matters and when you look at rules engines they never settled on a standardized way to control execution. There are numerous things that work, an advanced rules engine implements several, but none of them are 100% satisfactory for everyone.
One answer to these problems I see is "rules and schemes" where there are two programs that exist side-by-side, one that defines what is to be done and the other that fills in the details of how exactly it is done.
I can see something like that for the general programming language case. (Write a "style sheet" that explains how memory management is done) Also sometimes I fantasize that the way to achieve what Rust is trying to achieve is some combination of
(1) A really good macro assembler
(2) A theorem prover
(3) An ergonomic parser generator that makes writing rich DSLs easy and completely mainstream.
(1) is a good project (e.g. gas is by and for people who hate assembly language), (3) is a good project (I can rant about how bad parser generators hold developers back), the (1)-(2) punch is appealing with the the caveat that portability and optimization are problems in that case, but those could be addressed by developing architecture-specialized macro packs and (4) A general-purpose superoptimizer
which hooks into (2) which is itself a project the world needs.This is indeed the case! Rust's borrow checking is the first step towards "regions", a more general and more flexible model. TL;DR: We convey to the compiler that a certain set of objects are conceptually in the same region, and then we can turn immutable the entire region at once.
Pony and Forty2 [0] first came up with them, and Vale is building them into the language as first-class citizens and blending them with borrow-checker like semantics [1] to eliminate memory safety overhead.
It helps us do patterns that more traditional borrow checking has trouble with: observers, dependency references, delegates, callbacks, some forms of RAII, and intrusive data structures. This is mostly because regions get a lot of the borrow checker's benefits while still allowing mutable aliasing.
[1] https://verdagon.dev/blog/zero-cost-memory-safety-regions-pa...
For example, I once saw someone bring in an entire message-routing framework (Yew) because they couldn't get a simple observer to work within the borrow checker.
This is why I'm glad that Rust added Rc/RefCell; its design acknowledges that sometimes, the borrow checker gets in the way more than it helps, and it adds a more flexible alternative so we don't shoot ourselves in the foot by using the borrow checker in places where it doesn't make as much sense.