I read this article https://corrode.dev/blog/prototyping/ and it seems to address almost all of my concerns when I started learning Rust. I think it could take a beginner a long way until they need to get down to the borrow checker/lifetime. I said I agree with the blog "mostly" because there are situations where you have to interact with 3rd party libraries or APIs dealing with those lifetime. The, you need to know about those concept. If you know a better way to handle this, please let me know.
You could simplify rust slightly by sacrificing performance. For example you could box everything by default (like java) and get rid of `Box` the type as a concept. You could even make everything a reference counted pointer (but only allow mutation when the compiler can guarantee that the reference count is 1). You could ditch the concept of unsized types. Things like that. Rust doesn't strive to be the simplest language that it could be - instead it prefers performance. None of this is really what people complain about with the language though.
Personally I find it much easier to grok immutable data, not just understand when concentrating on it, then ownership rules.
What is hard is designing systems in a way resource ownership can be tracked and controlled without impacting performance. Rust makes it possible, but you can use smart pointers to give up speed and take simplicity instead. Most other languages assume (rightly so) you’re too dumb to do it correctly and give you smart pointers by default; some assume you’re smart enough and are proven wrong all the time (this is assembly and C relatives; actually they say ‘we don’t want smart pointers and we want a simple compiler, sucks to be you’).
Paradoxically, programmer life would be made simpler if there were some more complex borrowing rules, that would allow (for example) partial borrows of objects, or allow aliasing &mut in single-threaded circumstances where it's known to be safe (i.e. when the data is something primitive like an int, where it doesn't actually matter if it's overwritten while referenced).
But I know there's extra language design complexity that this introduces, and extra codegen complexity (Rust makes certain aliasing promises to LLVM that it isn't allowed to break) so it will take time. But, there are proposals in the works.
I've shipped three projects on it and they are pretty much as performant as they can be. I've never regretted skipping the lifetime work in application code.
https://github.com/mmastrac/keepcalm
I still dig into lifetimes for a lot of true low-level code but it doesn't need to exist at all at the high level
If anyone is considering using Rust and is nervous about lifetimes and bare metal, check out that article and try its guidance. I learned these things on my own the hard way and would have loved to read this article 18 months ago. It's really quite good.
As a beginner, you can avoid references (&) and simply clone() everything when it gives you trouble. If you start off by writing simple Actix/Axum web services instead of manually multithreaded apps, the problem domain is inherently linear and you'll avoid lifetimes and the borrow checker almost entirely. This lets you feel productive while getting a feel for the rest of the language features.
Don't do this once you learn the ropes of the borrow checker, of course. Once you grok it, the borrow checker is almost second nature.
Lifetimes you can probably get further without having to deal with. Just avoid storing references in structs and you will avoid a lot of lifetime headaches. Cloning can again be helpful here.
An alternative to cloning everything, if you are dealing with simple data types, is to derive copy for your structs so you can pass them around without worrying about ownership. It's not always possible though.
Smart pointers are another workaround, as others have said. But my problem with (some) smart pointers is that they simply move the checks to runtime, meaning now your code has a much higher chance of panicking at runtime.
They all make sense if you know why the program doesn't compile, but it may be surprising to newbies.
Borrow checker, well, this actually includes lifetimes. But let's say "basic ownership and basic borrowing", there is no way around starting with that, and it should be a point to learning rust.
There's nothing wrong with any of those languages (mostly) and not everything has to be written in Rust. IMHO the real value of Rust is as a systems programming language that's safer than C.
https://www.jetbrains.com/help/rust/rust-external-linters.ht...
The rust compiler never strives to never introduce breaking changes (by the definition I just described). It doesn't quite succeed (because some things like correctness are considered more important), but it fails
a) Very rarely, not once every 3 months.
b) In very small ways, that only break a tiny portion of code.
c) In ways that are very easy to fix.
d) Usually the rustc-devs will go offer patches to the entire open source ecosystem before any such release.
rustc is a rolling only compiler and that's not great and it does break often (not be able to compile code) in distros that are not rolling. And no, curl|sh and/or rustup are not solutions. I think the only solution is waiting for rust to become popular enough that the proportion of bleeding edge using devs to normal devs goes down.