Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

As someone who has gone deep into both functional programming (with Elixir, Clojure, and Scala) and also Rust, they solve the same problem in different ways with different tradeoffs.

The problem is that shared mutable state is incredibly hard to get correct, especially when concurrency enters the picture.

Elixir and Clojure sidestep the problem by making copying data so cheap that instead of sharing references to mutable data, you make all data immutable and copy it whenever you want to change it in some way. (Which is a classic technique: don’t like the problem? Solve a different problem that sidesteps the original problem in a creative way)

So you have a lot of functions of roughly the shape `f -> f` (return a new thing) instead of `f -> ()` (mutate a thing in place).

This possible at all by the clever use of some novel data immutable structures like HAMTs that are able to approximate the performance of traditional mutable data structures like hashmaps or arrays while presenting an immutable API.

As it turns out, this is a much easier programming model for most of us to get correct in practice (especially in the presence of concurrency) than sharing mutable state in an imperative programming language like C or Java or Python.

The tradeoff is that the immutable functional data structures actually do have a some performance overhead. In most domains it's not large enough to matter, but in some domains it is, and in those domains you really do need mutable state to eek out that last bit of performance.

Which is where Rust comes in.

In Rust's model, data can either mutable or shared, but not both at the same time. The Rust compiler computes the lifetimes of data throughout your program to ensure that this invariant is upheld, so mutable data is not shared, and shared data is not mutated.

The downside of this is that you have to internalize these rules and program to them. They can be tough to learn and tough to program with even after you have learned them, though it does get much easier with experience, I will say.

The upside of Rust's model is that you can have your cake and eat it too: you can keep the high performance ceiling of a true mutable data model while maintaining memory and resource safety.

In short, you can think of Clojure/Elixir as sidestepping the shared mutability problem at the cost of some runtime performance (though again in practice it is smaller than you would think), and Rust as tackling the shared mutability problem head on by transferring the cost of solving the problem to a more complicated compiler and a harder-to-learn programming model.

These are just my opinions having used both Rust and the immutable data functional programming stuff in anger. I'm not trying to present one as being better than the other, the key point is that they're both better than what came before.



My read today is that the advantage of Rust over C/C++ is that the compiler enforces the rules, where in C/C++ you'd have to use a static source code analyzer to find omissions. Since Clojure uses Software Transactional Memory and Persistant Data Structures to only make copies of updated shared data structures, it seems like it could actually be faster than Rust/C/C++ depending on how efficient the copy is. I may have it wrong but it sure is interesting...


While STM was a big selling point when Clojure in practice it's actually very rarely used. The persistent data structures are indeed the heart of Clojure.

While for many applications Clojure's performance is good enough it's not anywhere near what you can achieve with Rust. I once did a small game in Clojure trying to be very clever to eke out every last bit of performance and still didn't hit an acceptable frame rate. Made a very naive reimplementation in Rust that involved copying the entire state every frame and it run buttery smooth.

If there is a task for wish persistent data structures are the most performant solution it should be easy enough to implement and use them in rust too. Probably someone already did that.

Clojure is my default programming language but if I want performance (or static types) I reach for Rust.


That’s my take after using Rust for half a decade. The type system is very powerful and can encode logic in types in a way that’s impossible in most other languages, which is how the borrow checker works (the lifetime of a reference is part of its type).


> can encode logic in types in a way that’s impossible in most other languages

It’d be of great help if you could share an example of this along with an explanation why it’s impossible in a different language say one of Java/C++/Go


> in C/C++ you'd have to use a static source code analyzer to find omissions

Clang and GCC have a pretty solid suite of static checks that they can enforce if you enable them. They catch most of the common footguns.


It's worth noting that there's several implementations of persistent/immutable/functional data structures for Rust already, and a few basic "building blocks" (namely std::borrow::Cow, noted for its bovine superpowers) are even in the Rust standard library. It's possible that we'll see more of them standardized in the future if a case can be made that they're useful and a consensus is reached wrt. some kind of minimally viable APIs.


Did you look into zig and their solution ?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: