You can avoid this complexity by killing either one of the adjectives: shared OR mutable. Immutability is generally pitched as the only way to solve this problem, and it is definitely a valid solution, though not the only one. The other dimension to cut off is sharing. If mutable data is not shared, its mutability also becomes mostly irrelevant.
Rust and Swift both support immutability as well as unshareable references. In Rust’s case, it’s more like organized sharing that prevents simultaneous mutation. In Swift’s case, structs truly can only have one reference.
One does not replace the other, they coexist solving different problems.
When immutable values (whether it's the number 5 or a record, or whatever) are used with a pure function then the returned value can be essentially a direct replacement for the function invocation, making the operation essentially take zero time (i.e. there aren't intermediate mutation states, locks, or any other coordination).
This is the point that Rich Hickey suggests in a number of his talks [1] - which are always excellent IMHO.
[1] Are We There Yet - https://www.youtube.com/watch?v=E4RarTAZ2AY
The same can be said of any variable in Rust that is passed to a function via immutable reference. Borrow checking guarantees (at compile time, with no runtime overhead) that even if a user passes something that supports mutation (such as a BTree or an array) to a function, that that function may not mutate unless it has permission to.
This means that if you have an immutable reference to something, it can be treated as a value. For example: a Vec or BTreeMap (structures that supports mutation) can safely and easily be used as Map keys in Rust, because borrow checking guarantees that once the Map has taken ownership of the structure, it "is a value": ie, is immutable.
I don't think this is correct. Rust does not really have immutable references, it has shared references vs. exclusive ones. Shared references are ordinarily immutable, but controlled mutability can be reintroduced (directly or indirectly) via a variety of type constructors (Cell<>, RefCell<>, Mutex<> etc.). Hence something that is borrowed immutably cannot be assumed not to change in the general case-- unless one also takes care to work with these mutability mechanisms as needed, which cannot really be done in a "fully general" way. Rust developers seem to have realized that actual immutability is not that easy, and that the guarantees that they do offer may be more appropriate.
Clojure also recognizes the connection between ownership and mutability in its "transients": https://clojure.org/reference/transients ... compile time borrow checking extends that idea to an entire language.
But in Clojure it's not a problem. I find lots of applications where working with stale, consistent data is just fine.
Also, if you ever need to rewind your state Clojure's immutability makes it trivial. I've used this in e.g. latency compensation for multiplayer games.
The way you are forced to model things so that the borrow checker can successfully validate your code can often be frustrating, and it also comes at the cost of slow compile times and interactive programming, which Clojure is kind of a champion at, so I'm not sure it would be a good fit to add a borrow checker to it.
I love the borrow checker for manually managed memory though, and for programs that need that kind of performance and tight memory usage, it is great. I've been following along the development of Carp because of that and my preference of Lisps: https://github.com/carp-lang/Carp
Then, do not mark your variables as mutable. This error only happens if you use `let mut variable_name = ...`
AFAIK, `let var`, e.g. immutable variables, behaves the same as in clojure.
default behavior should be safe and simple to use, dangerous behavior should be hard to reach and inconvenient to use.
top comment claim is that clojure's approach to identity and value-semantics is somehow superseded and invalidated by rust's ownership and borrow checker, which is just too much koolaid. example i gave illustrates that given rust's default tools somebody will either have to deal with unnecessary low level errors (has nothing to do with high-level business logic of "i need this array to have new data") or will find a way to write incorrect code. none of that will happen given clojure's default tools.
But there have been very important developments in programming languages since this post/page was written: notably, the introduction of "borrow checking" (exemplified by Rust's implementation). Borrow checking has a very significant positive effect on the sustainability of imperative code, which makes the claim that "imperative programming is founded on an unsustainable premise" feel dated.
It is worth taking the time to understand what borrow checking enables. For example: borrow checking allows even mutable datastructures to be treated as values with structural equality. It does this by guaranteeing that unless you have exclusive access to something, it may not be mutated.
A good explanation of the benefits of ownership and borrow checking: http://squidarth.com/rc/rust/2018/05/31/rust-borrowing-and-o...