Flattening Rust’s learning curve

birdculture | 294 points

It's like reading "A Discipline of Programming", by Dijkstra. That morality play approach was needed back then, because nobody knew how to think about this stuff.

Most explanations of ownership in Rust are far too wordy. See [1]. The core concepts are mostly there, but hidden under all the examples.

    - Each data object in Rust has exactly one owner.
      - Ownership can be transferred in ways that preserve the one-owner rule.
      - If you need multiple ownership, the real owner has to be a reference-counted cell. 
        Those cells can be cloned (duplicated.)
      - If the owner goes away, so do the things it owns.

    - You can borrow access to a data object using a reference. 
      - There's a big distinction between owning and referencing.
      - References can be passed around and stored, but cannot outlive the object.
        (That would be a "dangling pointer" error).
      - This is strictly enforced at compile time by the borrow checker.
That explains the model. Once that's understood, all the details can be tied back to those rules.

[1] https://doc.rust-lang.org/book/ch04-01-what-is-ownership.htm...

Animats | 12 hours ago

It took me a few tries to get comfortable with Rust—its ownership model, lifetimes, and pervasive use of enums and pattern matching were daunting at first. In my initial attempt, I felt overwhelmed very early on. The second time, I was too dogmatic, reading the book line by line from the very first chapter, and eventually lost patience. By then, however, I had come to understand that Rust would help me learn programming and software design on a deeper level. On my third try, I finally found success; I began rewriting my small programs and scripts using the rudimentary understanding I had gained from my previous encounters. I filled in the gaps as needed—learning idiomatic error handling, using types to express data, and harnessing pattern matching, among other techniques.

After all this ordeal, I can confidently say that learning Rust was one of the best decisions I’ve made in my programming career. Declaring types, structs, and enums beforehand, then writing functions to work with immutable data and pattern matching, has become the approach I apply even when coding in other languages.

8s2ngy | 8 hours ago

Rust has a few big hurdles for new users:

- it's very different from other languages. That's intentional but also an obstacle.

- it's a very complex language with a very terse syntax that looks like people are typing with their elbows and are hitting random keys. A single character can completely change the meaning of a thing. And it doesn't help that a lot of this syntax deeply nested.

- a lot of its features are hard to understand without deeper understanding of the theory behind them. This adds to the complexity. The type system and the borrowing mechanism are good examples. Unless you are a type system nerd a lot of that is just gobblygook to the average Python or Javascript user. This also makes it a very inappropriate language for people that don't have a master degree in computer science. Which these days is most programmers.

- it has widely used macros that obfuscate a lot of things that further adds to the complexity. If you don't know the macro definitions, it just becomes harder to understand what is going on. All languages with macros suffer from this to some degree.

I think LLMs can help a lot here these days. When I last tried to wrap my head around Rust that wasn't an option yet. I might have another go at it at some time. But it's not a priority for me currently. But llms have definitely lowered the barrier for me to try new stuff. I definitely see the value of a language like users. But it doesn't really solve a problem I have with the languages I do use (kotlin, python, typescript, etc.). I've used most popular languages at some point in my life. Rust is unique in how difficult it is to learn.

jillesvangurp | 5 hours ago

> Stop resisting. That’s the most important lesson

> Accept that learning Rust requires...

> Leave your hubris at home

> Declare defeat

> Resistance is futile. The longer you refuse to learn, the longer you will suffer

> Forget what you think you knew...

Now it finally clicked to me that Orwell's telescreen OS was written in Rust

GenshoTikamura | 2 hours ago

If a language needs an article like this, absolutely begging people to bite the bullet to learn it, maybe that's a language design smell.

Disclaimer: I haven't taken the time to learn Rust so maybe don't take this too seriously..

mdwhatcott | 11 hours ago

Feels like a cult manual. "Do all things our way! Don't question anything!"

Meneth | an hour ago

One approach that I don't see often enough is to focus on learning a subset of the language first. For example, in my own book on Rust, I skip teaching lifetimes. It's not necessary to write functions with lifetimes to build quite a few fully functioning programs. The same with macros (although the fact that their signatures are opaque doesn't make it easy for beginners). On the other hand, I disagree with the advice to rely on copy() or clone() - it's better to learn about borrowing from the beginning since it's such a fundamental part of the language.

mprovost | 3 hours ago

"Safe" Rust is generally a simple language compared to C++. The borrow checker rules are clean and consistent. However, writing in it isn't as simple or intuitive as what we've seen in decades of popular systems languages. If your data structures have clear dependencies—like an acyclic graph then there's no problem. But writing performant self-referential data structures, for example, is far from easy compared to C++, C, Zig, etc.

On the opposite, "Unsafe" Rust is not simple at all, but without it, we can't write many programs. It's comparable to C, maybe even worse in some ways. It's easy to break rules (aliasing for exmaple). Raw pointer manipulation is less ergonomic than in C, C++, Zig, or Go. But raw pointers are one of the most important concepts in CS. This part is very important for learning; we can't just close our eyes to it.

And I'm not even talking about Rust's open problems, such as: thread_local (still questionable), custom allocators (still nightly), Polonius (nightly, hope it succeeds), panic handling (not acceptable in kernel-level code), and "pin", which seems like a workaround (hack) for async and self-referential issues caused by a lack of proper language design early on — many learners struggle with it.

Rust is a good language, no doubt. But it feels like a temporary step. The learning curve heavily depends on the kind of task you're trying to solve. Some things are super easy and straightforward, while others are very hard, and the eventual solutions are not as simple, intuitive or understandable compared to, for example, C++, C, Zig, etc.

Languages like Mojo, Carbon (I hope it succeeds), and maybe Zig (not sure yet) are learning from Rust and other languages. One of them might become the next major general-purpose systems language for the coming decades with a much more pleasant learning curve.

cyber1 | 3 hours ago

Rust is wonderful but humbling!

It has a built in coach: the borrow checker!

Borrow checker wouldn't get off my damn case - errors after errors - so I gave in. I allowed it to teach me - compile error by compile error - the proper way to do a threadsafe shared-memory ringbuffer. I was convinced I knew. I didn't. C and C++ lack ownership semantics so their compilers can't coach you.

Everyone should learn Rust. You never know what you'll discover about yourself.

cadamsdotcom | 11 hours ago

My problem with rust is not the learning curve, but the absolute ugliness of the syntax. It's like Perl and C++ template metaprogramming had a child. I just can't stand it.

Python is my favourite, C is elegance in simplicity and Go is tolerable.

TheChaplain | 4 hours ago

Ownership and lifetimes are arguably not about Rust, but about how we construct programs today. Big, interdependent, object graphs are not a good way of constructing programs in Rust or C++.

writebetterc | 3 hours ago

>For instance, why do you have to call to_string() on a thing that’s already a string?

It's so hard for me to take Rust seriously when I have to find out answers to unintuitive question like this

baalimago | 6 hours ago

A learning curve measures time on the x axis and progress on the y axis.

A flat learning curve means you never learn anything :-\

ants_everywhere | 12 hours ago

The whole learning curve seems overblown to me. You don’t need to grok every part of the language to start using it and being productive.

jamil7 | 5 hours ago

I thought it was quite manageable at beginner level…though I haven’t dived into async which I gather is a whole different level of pain

Havoc | 12 hours ago

Is there a concise document that explains major decisions behind Rust language design for those who know C++? Not a newbie tutorial, just straight to the point: why in-place mutability instead of other options, why encourage stack allocation, what problems with C++ does it solve and at what cost, etc.

sesm | 11 hours ago

This reads like a list of symptoms of what's wrong with the ergonomics of Rust. This is not to bash Rust. It has its uses. But you need to balance what you are sacrificing for what you are getting.

doug_durham | 7 hours ago

Write a CHIP8 emulator!

Bonus: do it with no heap allocation. This actually makes it easier because you basically don’t deal with lifetimes. You just have a state object that you pass to your input system, then your guest cpu system, then your renderer, and repeat.

And I mean… look just how incredibly well a match expression works for opcode handling: https://github.com/ablakey/chip8/blob/15ce094a1d9de314862abb...

My second (and final) rust project was a gameboy emulator that basically worked the same way.

But one of the best things about learning by writing an emulator is that there’s enough repetition you begin looking for abstractions and learn about macros and such, all out of self discovery and necessity.

Waterluvian | 11 hours ago

Good article, thoughtful and well-written and (idioms aside) much of it applicable to learning other languages.

mellosouls | 6 hours ago

Traits are yet another way of doing the CS concept of interfaces, regardless of the author's opinion.

Polymorphic dispatch on a set of known operations, that compose a specific type.

pjmlp | 3 hours ago

What is it that make that rust is said to have steep learning curve compared to other programing languages (in the same category)?

MasterYoda | 6 hours ago

> Use String and clone() and unwrap generously; you can always refactor later

At that point you might as well be writing Java or Go or whatever though. GC runtimes tend actually to be significantly faster for this kind of code, since they can avoid all those copies by sharing the underlying resource. By the same logic, you can always refactor the performance-critical stuff via your FFI of choice.

ajross | 9 hours ago

Regarding the first example, the longest() function, why couldn't the compiler figure it out itself? What is the design flaw?

CobrastanJorji | 11 hours ago

For me the most important thing about Rust is to understand that it's a langue with value semantics. Which makes it completely different than every mainstream language you encountered so far.

Variable in rust is not a label you can pass around and reuse freely. It's a fixed size physical memory that values can be moved into or moved out of. Once you understand that everything makes sense. The move semantics, cloning, borrowing, Sized, impl. Every language design element of rust is a direct consequence of that. It's the values that get created, destroyed and moved around and variables are actual first-class places to keep them with their own identity separate from values that occupy them. It's hard to notice this because Rust does a lot to pretend it's a "normal" language to draw people in. But for anyone with experience in programming that attempts to learn Rust I think this realization could make the process at least few times easier.

It's hard to shift to this new paradigm and embrace it, so in the meantime feel use a lot of Rc<> and cloning if you just need to bang out some programs like you would in any other mainstream language.

scotty79 | 3 hours ago

Compared to other programming languages, Rust's compiler and linters go a long way to implement best practices at build time.

truth_seeker | 5 hours ago

> …. For instance, “a trait is a bit like an interface” is wrong, …

Wait. What's wrong with this statement?

umajho | 4 hours ago

> Treat the borrow checker as a co-author, not an adversary

Why would I pair-program with someone who doesn’t understand doubly-linked lists?

dmitrygr | 13 hours ago

Surrender! to compile

Weather the ferocious storm

You will find, true bliss

worik | 9 hours ago

[dead]

commisson | 16 minutes ago

Does anyone still have trouble learning Rust? I thought that was kind of a 2015 thing

woah | 12 hours ago