Rust is both algol-derived, and ocaml-derived. And arguably the borrow checker creates a new paradigm entirely.
I don't think this can be emphasized enough. You can get deceivingly far in rust with an OOP style, only to come to the disheartening conclusion that it's impossible to do what you want with the architecture you spent months of work setting up.
For example, how would you architect a simple single threaded emulator? The CPU is an object, RAM is an object, easy enough. Okay, the CPU needs mutable access to RAM at all times, so we'll make the CPU own the RAM, sure why not? Okay now we want to implement a graphics device that can mutably access RAM, so we just...put the ram out on it's own and make it a Rc<RefCell<T>>?!? Now it takes three lines of code to unwrap all the smart pointers to write a single byte of memory?!? And it's slower because we have to pay for runtime borrow checking!
I went down exactly this road. I still use rust and love many things about it, but I've come to the conclusion that writing typical single threaded object oriented code in rust only looks like it works; it's an awful idea in practice.
I think the reason C++ people have so much trouble with rust is the syntax makes it easy to do the things you've always done, but the mysterious borrowchecker tells you no later on.
That was the biggest shock I had moving to Rust: I couldn't just do what I wanted, I had to figure out how to do what Rust wanted. But I also wanted to learn how to design systems better, so the fact that Rust makes me think deeper about it is an advantage for me. In an OO language, I'd just lazily cobble things together. The code would work, but I would learn nothing.
And some people would rather do things that way. It is very hard to break habits, and Rust basically forces you to do that.
If we are talking about emulator, why not just make clear communication between CPU, RAM, and graphic device? Memory can be split into rows (e.g. by split_at()), so graphic device can take ownership of a single scan line at a time, leaving majority of the memory unlocked. We cannot avoid problem, but we can make scale of the problem much smaller by splitting memory into pages, so CPU can take ownership of pages, it works with right now, while graphical device can work with it own pages.
>It's hard to implement unsafe patterns in safe language.
I agree with this 100%. And I think that for certain problem domains, rust provides more safety than you actually want.
Well, those are not the issues that concern me, or that most (in blogs/HN posts/comments/etc I've read) consider the hard parts. It's the borrow checker.
The borrow checker might seem like the problem, but the solution is to learn to code in a new style.
I worked on a large C++ code base (some 15 years ago, before Rust existed) which did this kind of thing: it had references enforcing single ownership that was handed on assignment or passing.
Borrowing was of course done in the usual way, by passing down references (most often const ones).