Rust is too hard to learn

Regarding "maybe not a great first language?", I have a strong suspicion that Rust might actually be more approachable as a first language than as a second one. People (myself included) get into mental ruts. Especially after you've learned a couple languages that all do things more or less the same way, switching gears can be a challenge of its own, whereas if you come to Rust with few preconceptions and are introduced to it one concept at a time, the way you might be in a typical introductory programming class, it's not much more complicated than Java, and less complicated than C++ or Python, in some ways.

That said, different people find different things challenging, as anyone who's been on this board for long will know. Some people pick up lifetimes fairly quickly but struggle to design programs without using class inheritance. Some people are just the opposite. Some people have to wrestle with each concept that other people just intuitively understand.

@rafaelcout, if what you're dealing with is "lifetime hell", it's probably because you're using references where they're not necessary or appropriate. People who are new to Rust often overuse references because they haven't understood the semantic differences between owning and borrowing a value, or haven't designed their program with ownership in mind (both things you should do in any language, by the way; Rust didn't invent the concept of ownership, and in some ways it's even more important in languages like C++ that have no compiler guardrails).

If you're comfortable sharing the code, many of us would be willing to take a look at it. There's a good chance the design can be simplified to reduce and avoid lifetime problems.

5 Likes

Fair enough – I was only wondering that because I usually see people struggle with Rust due to not understanding explanations that build on background knowledge common to Rust and other languages.

That said, the "mental rut" is absolutely a realistic problem, so I guess it boils down to the trade-off between the two.

2 Likes

Yeah, I mean, I'm mostly just speculating. I don't know of any courses/books that teach Rust to complete beginners (along the lines of Head First Whatever, Learn You a Haskell, etc.) and I don't know any programmers who have learned that way, so I don't even have anecdotal evidence to support the theory.

1 Like

I was just wondering about that. As in "what is that background knowledge?"

Much of understanding Rust is basically all about memory. What is the stack? What is the heap? Where is everything? Simple things like a String being both a structure on the stack and the actual string content on the heap.

The background knowledge then is how our processors actually work, how they work with memory, how functions become actual code and how that code refers to memory.

Harking back to my early days that background knowledge came by way of the fact that I was following a "computer science" course, all be it at a kind of high school level. We were made aware of how processors worked, instructions, memory, ALUs etc. All be it in a very simplified way.

Seems to me many programers today, having only been exposed to Java, JS, Python etc, have never been aware of such computer mechanics.

8 Likes

Stick with it. Lifetimes are a new paradigm if you're coming from languages like C, C++, Java, Python, etc. It will eventually "click" for you after more practice. It was difficult for me to grasp too. I simply avoided putting references in my structs for the first 6 months of learning the language because I just didn't get it.

One thing that helped me start using lifetime specifiers correctly was to give them meaningful names instead of the standard 'a, 'b, etc. For example, instead of doing this:

struct MyStruct<'a, 'b> {
    credentials: &'a Credentials,
    cookie: &'b Cookie,
}

I started doing it like this:

struct MyStruct<'credentials, 'cookie> {
    credentials: &'credentials Credentials,
    cookie: &'cookie Cookie,
}

Doing it this way helped me understand that the lifetime specifier is tied to the thing it is referencing. 'credentials is the lifetime of the struct that the reference credentials points to.

11 Likes

I'll add my vote for the compiler and borrow checker being a feature of Rust that I've finally come to appreciate. Also, learning Rust as a second full language was great, but only once I realized I needed to break all my assumptions about programming down to basics to figure out how to write things correctly. I'm starting to think of the compiler and borrow checker as training wheels for writing correct code, which I'm trying to end up relying on less and less. While they can definitely feel like a wall, you just have to find the doors.

8 Likes

Interesting. I learned Rust without ever thinking about the stack and heap. What slowed me down was thinking the borrow checker was all about data races. Things fell into place once I realized it was all about memory management.

5 Likes

If you don't need to ensure, that the original data is kept alive, make use of Clone and Copy. If you need shared ownership, then use Rc<_>, Rc<Cell<_>>, Rc<RefCell<_>> for single-threaded code and Arc<_>, Arc<Mutex<_>>, Arc<RwLock<_>> for multi-threaded code.

It's much easier to find alternative solutions than fighting the borrow checker. A good starting point to learn why lifetimes are important is a solid knowledge base around the stack and the heap.

Lifetimes, as Rust refers to them, don't exist in more abstract languages like Java, because they forbid the creation of pointers to values on the stack. If you pass around a value in Java, you actually pass around a pointer to a value stored on the heap. This makes these languages easier to learn, but also limits them in other ways such as performance, latency and memory usage.

On the other side of the spectrum are languages like C, which also don't have lifetimes as a part of their language design, but in practice the lifetime model Rust uses still has to be applied to stack pointers in C, except those languages make it your task to ensure the validity of the program.

Last but not least there's Rust, which combines the advantages of guaranteed memory safety as Java without its accompanying disadvantages, resulting in Rust being able to compete with C and C++ in many areas where Java and co. can't shine. However, nothing comes without a cost and for Rust this cost comes in the form of increased complexity beginners have to face when learning about and using references.

I don't have a complete guide / tutorial / book focusing on this topic, made for beginners, yet. For now, I can only recommend asking questions on this forum, as others have already pointed out.

2 Likes

I found Rust pretty easy to learn once I jettisoned some OO baggage I had been dragging with me.

6 Likes

Ok, I will avoid storing references directly in structs and enums for now and I will be more present in the forum, thanks.

9 Likes

Good plan. Remember many of us here are not far ahead of you in experience in Rust, the hurdles are still fresh in our minds, so you are bound to find a sympathetic ear here.

Meanwhile I found Jon Gjengset's presentation on lifetimes nice an easy on the mind: Crust of Rust: Lifetime Annotations - YouTube.

As is Ryan Levick - YouTube : Understanding Rust Lifetimes - YouTube

5 Likes

I just started learning Rust 4 days ago and I must say that I'm very impressed with the book. It strikes the perfect balance between enough information, but not too much so as to render it verbose and unmanageable for a newbie. I'm up to Chapter 7 and I'm hoping to finish it in the next week or so so that I can continue trying to write my first app :slightly_smiling_face:.

If you haven't read the book - the whole book - you should start there. It's definitely not like a high-level language where you can just start hacking away after Googling a few things and then slowly figure it out on your own. This is a good thing though - I wrote working JavaScript code (but far from optimal, and sometimes buggy :wink:) for years without ever understanding it until I read Kyle Simpson's wonderful You Don't Know JS books. It appears to me that such an approach simply won't work in Rust - you'll never leave the ground floor.

Whether or not it's "difficult" or "hard" depends on your perspective. I realized very quickly (somewhere around halfway through day 2 when I was beating my head against my desk :rofl:) that my time would be better spent reading the book all the way through, writing all of the toy examples that are demonstrated, etc. Only then am I going to get back to the app I'm attempting to write in Rust (what I thought would be a fairly simple CLI that takes some user input, calls an API, and writes the JSON response to a file). If I would have only spent the time up front to read some good books on JavaScript, I would have been a much better JS programmer much earlier on. I won't make this mistake with Rust, because Rust will not let me make that mistake. Furthermore, the book is so good IMHO that I don't feel the need to search for books or courses to purchase. To learn JavaScript and many of the tools in its ecosystem (Node/React/Vue/etc.) I've purchased many books, paid for numerous courses on Udemy, etc., but I'm not seeing the need for that with Rust (at least not yet).

You'll soon see lots of questions from me on here. Thanks in advance! ; )

7 Likes

In the wise words of @kornel , "don't use lifetimes until you think rust is easy". Once you begin to understand the architecture of your program from a birds-eye perspective, then lifetimes become pretty useful and intuitive

11 Likes

It is really hard to learn!

1 Like

Sorry, I admit I didn't read the whole thread yet (I will do that next), but I felt like responding first anyway.

I love the title you chose for this post, Rust IS definitely very hard to learn. I have learned not some of the oldest languages, but many other ones, and out of everything I studied, Rust was harder than all of those, and I didn't learn every aspect of it yet, even after studying it for about a year and even after using it for some real-world applications.

Although it is very hard to learn, there aren't too many parts of it that jump out at me as being things I would really want changed.

It's absolutely a valid criticism though, Rust being too hard to learn, and it's preventing the language from being used in more software houses, but I personally don't care about this, because it means I can put in the effort to learn something hard which other people won't bother learning, and then I can get ahead and get higher pay than others for the same amount of daily work in the places which do hire Rust programmers.

There is a good reason too (I am probably repeating others here, sorry) why it's hard to write code which compiles, and they outline that in the official free Rust book. The compiler wants more of the compile-time code to be correct, so that once we've moved to run-time, we can have more guarantees of various aspects of a program being correct and not crash-prone, or prone to other errors.

It's a trade-off, but to me a compiler error is much faster and easier to notice, fix, and validate, than most any kind of run-time bugs which would need certain conditions to be hit while the program is running and things needing to be watched through a debugger to validate the correctness. So I'm happy for those things in Rust, because less run-time debugging means a lot less annoying work and less unexpected bugs slipping through QA.

Regarding "LIFETIME HELL", I think it's not really hell, it's just an extra thing which many other languages don't give you explicit control over, so it's yet one more thing to have to think about when writing Rust code. But I think while it's not immediately intuitive, it becomes pretty simple to understand after practising writing some real-world Rust programs for maybe a month or two. It used to be worse too, these days in Rust, a lot of lifetimes can be automatically filled in when you want a default lifetime behaviour, it didn't used to be that simple in older versions of Rust.

I don't find the compiler messages nearly as cryptic as for example, the C++ compiler when it's complaining about template metaprogramming errors. One thing which is great about the Rust compiler error messages that a lot of other languages' compilers don't do, is it will often suggest other similarly named functions or variables when the one you put caused an error, and sometimes these suggestions have saved me a lot of time looking through various files to find the right name for something I had done wrong. It also usually gives at least two different line numbers to consider for any given error, something similar to "this thing you did on line NN is wrong, maybe because of this other thing over here on line MM". These bits of coding advice direct from the compiler error messages have been really helpful to me in learning how to write Rust code properly, and I love them.

Anyway, sorry if I repeated some things which other people already said. I will actually read the thread now. :stuck_out_tongue:

3 Likes

Rustlings - Small exercises to get you used to reading and writing Rust code! is a very good resource that helped me a lot.

1 Like

I like to see actionable advice for lifetimes, like "don't put lifetimes in structs as a beginner".
I've not had a chance to use this advice from @Yandros yet, but it sounds promising:

4 Likes

I feel the same way, but I'm afraid that's not Rust's fault. You may not be able to make it any simpler. Rust already made everything simpler than C/C++, a ton really. You may not feel or see it at first, because that's where C/C++ is faking its ease of use in some regards. Like you said it yourself, life times.

With C/C++ it may be simpler at first, but only because they are not as strict at compile time as Rust is, as already mentioned multiple times by others here as to why in the first place.

I'm afraid, if you need it simpler, your only choice is a managed code language. Managed code handles the memory allocations, so ownership and life times for your. But of course, there is again a price to pay with a managed code language. And it's at run time as your speed decreases and memory foot print increases. Code safety is as good or perhaps better than what Rust could guarantee you at run time, but certainly is much more safer than C/C++.

Well, for the future, since we live in the artificial intelligence era. With the help of AI there may be a language that is purely algorithmic and leaves the lower level coding to the AI. It could be as simple as managed code but as fast as assembly. Currently I'm not aware that anybody works on such a language or rather AI driven compiler.

1 Like

That's terrible advice, because there are many reasons to put lifetimes in structs. As a beginner, you should be putting lifetimes in structs so that you can learn how to do it correctly. Nobody is going to become a competent rust programmer by ignoring its most fundamental and compelling feature.

It's not even a complicated thing. A lifetime parameter on a struct means that struct can only exist if the references it contains are guaranteed to be valid for the lifetime of the struct. It's a very common-sense thing, and it is something a beginner needs to learn right away, or else they have no reason to use Rust in the first place.

2 Likes

This is hyperbole. References and lifetimes are helpful even in simpler cases like tying return values to parameters and making containers local to functions which contain references. And even if they don't use them directly themselves, they help when they're using libraries -- XML parsers that return references to internal buffers are actually safe, for example.

Will they plausibly make structs with lifetimes shortly after they get comfortable with the basics? Sure. But the first time they make a User struct, it really should have a String in it, not a &'a str.

3 Likes