Burnout whilst learning the language

Dear all,

I've been curious to try the Rust language ever since I first learnt of it. I haven't exactly had trouble with esoteric programming languages - my first job was using Erlang, and the most recent language I learnt for fun was Perl, of which I am quite fond.

Latterly I have been offered a chance to lay down a technological foundation for a burgeoning software company, and I wanted to pick something suave, something with promise that would also play well on Linux.

I was delighted to see that Rust ticked all my boxes. It is very fast and has lots of webdev goodies like web frameworks and ORM. There's even a plugin for IntelliJ.

So I spent one month and a half learning it in my spare time, making my way through the online book to about half way, when panic set in and I realised that I needed to build something related to the business, I needed to see something tangible as we lurched towards a deadline. Thus I've reverted to a more familiar language to knock out a demo site.

I guess there is this fear that I haven't quite absorbed the concepts - the traits as return types, the error handling, the lifetimes - even though they look powerful and I would love to work with them. I found with Perl that it only made sense when I actually built something in it, and I gather that it would be the same with Rust.

It isn't all over, I could use $BORING_LANG to build the demo site and rewrite it in Rust if we actually get funding and I work on it full time. Perhaps a good week or two of steady learning would be enough to learn the language, then after I could start on the rewrite.

But what have your experiences been? How long did it take you to 'get' Rust, where you could 'think' in Rust and write in it confidently? Has anyone else been here before?

Apologies if this is too wordy or personal, but I don't want to give up on the vision.

Regards, L.F.

5 Likes

Rust had a really long learning curve for me, but I found it to be a relatively gentle one. I started by writing a few hobby programs in Rust, which was enough to become confident in getting results, even though I was avoiding large parts of the language and writing unidiomatic code.

It was enough, however, for me to choose Rust for the project in my compilers course and later my Master’s thesis. Only through those projects did I get to the point where I felt like I “got” Rust fully¹. Most importantly, I never got the feeling that I wouldn’t be able to finish these two sizable projects on time, despite my occasional struggles with the language.

¹ That’s an exaggeration: I’m still not comfortable with some of the unsafe rules.

5 Likes

I also struggled very much trying to learn Rust. I gave up three times (literally). But when made a fourth attempt, I finally managed to get along.

To me, it was difficult to get complex things working. Nowadays I know that I sometimes need to resort to things like Arc or synchronization utilities or things like RefCell to work my way through the strict borrowing concepts. But it's worth it.

When learning the language, Arc, Rc, Box, and the like seemed a bit "esoteric", but you really need them (at some point when making some "real" program).

But even after thinking I had a good understanding of the software, I sometimes stumble upon surprising aspects. But I feel confident enough now to create programs, libraries, etc., and I prefer using Rust over any other language.

4 Likes

Rust certainly has a steep and long learning curve for many, and I include myself among them, but most of the time after you stop fighting the borrow checker there's this "eureka" moment where its abstractions start making sense to you.

After that, it has been the most pleasant experience I've ever had with a programming language. Considering that it has been voted as the most loved programming language in StackOverflow's survey for 7 consecutive years (and counting), I guess this feeling is shared by many, many programmers around the world.

Word of advice: don't give up. If you struggle with any concept, try finding different sources of information around the subject like videos, articles, SO questions. The more, the better, because some will go really deep while others will just scratch the surface in a digestible manner for you to get started with.

4 Likes

In my opinion, another thing that can be tricky when learning the language is that Rust sometimes hides the fact whether something is a value or a reference to that value. Let me share an example:

fn takes_str(value: &str) {
    println!("{value}");
}

fn main() {
    let s: String = "Hello".to_string();
    let r: &String = &s;
    let t: &str = &s;
    let len1 = s.len();
    let len2 = s.len();
    let len3 = s.len();
    takes_str(s); // doesn't work
    takes_str(r);
    takes_str(t);
}

(Playground)

Calling .len() works in all three cases, but we can only pass r and t to takes_str, but not s.

To fully understand what's going on, one needs to always aware of the types being used – and bear deref coercion in mind (which allows passing r to takes_str, even though r is of type &String). This is really tough when you're beginning learning Rust (and feel safe with all the .len() calls and don't care about whether something is a reference or of which exact type is a value).

Rust is a complex language. It is even harder to learn on your own. For me, it took more than half a year before I felt fully confident in using it for my job (and let me tell you, that confidence was overestimated). It can be much more gentle if you have a mentor and join an existing well-structured project, but I would still expect 1-2 months to get really up to speed. May be a bit more or less depending on your previous background.

Overall, this means that learning Rust in your spare time and building a demo for a new project is likely a recipe for disaster. At the very least you should be learning it full-time on the job, if you expect to hit that deadline.

I love Rust, and once I learnt it, I feel that I'm at least as productive as in any other language (including something like Python, which prides itself on developer productivity). The tooling is great, the ecosystem is solid, and the language design choices, while requiring some getting used to, save you from a lot of trouble in the medium-long term. However, learning it while doing an important project is a very problematic proposition. It is a good idea to make a demo using the technologies that you already know, and learn Rust in the meantime without pressure.

You can try writing some small command-line utilities if needed. This is a best-case for Rust, because it's relatively simple, doesn't usually require the more complex features (like async), the ecosystem has strong support for that use case, and most importantly the project is small enough that you won't need to live forever with your mistakes while learning the language (and you can be sure that your initial projects will be full of suboptimal choices and architectural landmines).

10 Likes

Almost everyone.

Couple of months, maybe three months once I have started writing some toy projects.

Try to translate medium-sized projects from any other language into Rust. Or write from scratch.

Most likely first attempt (or three) would fail. And next attempt (or three) would be ugly.

But after half-dozen rewrites you would understand how most things in Rust work and why they work like they do.

Don't try to achieve perfection on the first try. Rust makes it quite safe to do serious refactorings, use that to your advantage.

My first non-trivial program was conversion of some non-trivial C++ TMP code into Rust.

And that was quite an experience. Things which were trivial in C++ required crazy complicated things in Rust (I even needed procmacro to be able to finish the conversion), while things which are complicated in C++ turned out to be trivial in Rust.

In the end I've got library which looked very superficially similar to what I had in C++ on the outside, but had quite different composition on the inside.

That's something which both makes Rust “the most language on StackOverflow” and makes it hard to learn: Rust rejects certain extremely popular mistakes which popular computer languages accumulated over the last half-century. Starting with the billion dollar mistake.

Mind you: Rust doesn't fix all the known issues with popular languages. If it did it would have been as popular as Idris or agda (IOW: not popular at all), but it fixes a lot.

That's why it takes time to accept it. You need to change your habits. That's never easy.

3 Likes

I wrote C and C++ for a long time before discovering Rust. After having invested a lot of time into learning modern C++ best practices, Rust's memory model immediately felt all natural. I didn't feel that the language was painful to learn or that the ownership and borrowing model would have been an obstackle. Generics and type-level programming were a breeze compared to C++ templates.

Perhaps the only part of the language I procrastinated learning was declarative macros, because their implementation looks syntax-heavy on the outside. Of course once you dig just a bit deeper, it's not too bad either.

Overall, I felt comfortable writing non-trivial Rust after a couple of months. I did not have the expectation to rewrite entire libraries in Rust at day 0 though, and I was learning the ecosystem piecemeal. (A big part of knowing Rust is knowing your libraries.)

One corner of the language I'm still not proficient with is unsafe outside FFI. Compared to C and C++, there are significantly more rules for avoiding UB. But that's fine. I have been using Rust both for fun and at work for several years, and the only situations when I genuinely needed unsafe was FFI, so I'm OK with std and a few trusted crates doing the unsafe heavy lifting for me.

My advice would be to consider what will happen once you are proficient in the language. Getting a compile error is not the end of the world – it is simply how statically-typed compiled languages work. Even the best programmers encounter many, simpler or more complex compile errors all the time while working on real code, this is only natural, and it doesn't necessarily mean that the language is fighting you. When getting an error, don't say "I don't know why this happens, this is annoying". Instead, ask yourself, "why could this possibly be happening?" – regarding it as a puzzle rather than a failure makes the learning process much more satisfying.

11 Likes

Indeed, often experienced programmers will intentionally write code both to make it more likely they get compile errors later, or even trigger them intentionally to take advantage of rustc's great error messages.

For example, you can write your functions as -> _ at first, write the body, and then have the compiler tell you what to put there

error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
 --> src/lib.rs:1:14
  |
1 | fn demo() -> _ { vec![12, 34, 56] }
  |              ^
  |              |
  |              not allowed in type signatures
  |              help: replace with the correct return type: `Vec<i32>`
5 Likes

I have spent a lot of time teaching people to program (mostly in Python and C++). This has taught me that some people consider being confronted with a compiler or run-time error to be a sign of personal failure: a statement about their inadequacy as a person.

If anybody reading this falls into this category, please, DON'T do that!

You should consider the error messages generated by your language to be one side of a collaborative
conversation
about how to create a working, useful, reliable piece of software.

[Edit: and you should bear in mind that your interlocutor means well, but isn't very skilled in communicating with people.]

When helping someone I'm teaching to solve a problem, I often say something along the lines of

Here we need to do this, then we need to do that over there, and then ... well ... erm, let's just ask the compiler what needs to be done next.

The coin didn't really drop for me (about the existence of the aforementioned attitude) until an occasion when my interlocutor, physically preventing me from presenting the compiler with half-baked source code, said something that (with some artistic licence) translates to

Noooo! But, but ... then we'll get another compiler error, and I'll have to flog myself for penance this evening. And I've already accumulated a lot of floggings today. Pleeeeease, don't compile this, I BEG you!

TLDR: treat your interaction with the compiler as a friendly conversation, not as a dressing down.

13 Likes

To be honest, some programming languages’ compilation errors are so cryptic and clearly not designed for human consumption that I wouldn’t blame users for believing they failed / did something wrong and weren’t intended to get that compiler output. The unfortunate side-effect of this with respect to Rust can be that with such experience, people tend not to properly read Rust’s error messages either, expecting nothing useful out of taking a closer look, until they (hopefully) eventually learn otherwise.

6 Likes

Totally agreed.

Another thing I find myself doing over and over again in teaching situations is to sympathize with the natural human reflex to avert one's eyes when presented with compiler errors, and to point out that sometimes overcoming this reflex and trying to extract meaningful information from the message, can be the quickest and least painful way to progress. Not always. Sometimes it's just a painful waste of time.

But, these error messages are improving. There was a huge qualitative leap when Clang appeared on the scene. Rust and Elm and others, have greatly improved things too. I was pleasantly surprised to find that recent Python, g++ and clang versions seem to have made quite some progress too. I don't recall any of these suggesting possible corrections for near-miss name errors before, and both Python and C++ are doing it this week!

There's plenty of scope for improvement, but there is progress in the right direction.

Here's my filthy admission: I've never read the book, at least actually working my way through.

My approach was to simply write lots of toy programs with heavy use of the reference and Google, until I thought I knew enough to use it at work. I didn't, of course, but that bad code is far better than my early JavaScript, python, etc., so I'm not too broken up about it: it's really just that it took too long and should have used way more crates.

So the TLDR is just write the bad code. You're going to need to at some point!

2 Likes

Thanks very much for these replies. Quite a lot of thoughtful comments here. I'm sure they'll be helpful to more than just myself!

In short, the plan will be to write the demo app in a known language. Once that's done I'll have time to spend learning Rust again. Later it will be just a matter of swapping the backend code for its replacement in Rust; the frontend won't care, it's all JSON anyway.

2 Likes

I may be coming from a different perspective here and in no way claim to be an expert at Rust. In fact, several of the people who have replied here have also helped me solve issues accomplishing tasks on this forum. I agree with the fact that the learning curve can be steep, particularly if you are not working with a team already proficient in Rust.

I started out a couple of years ago by going through the book, coding the examples and fiddling with bits of code in the playground to try things out. I found the playground really useful to just try things out before coding them and still use it all the time.

After the book, I went through Advent of Code 2020 (in 2021, not quickly nor competitively). This was really useful for data structures and algorithms. I looked at other peoples solutions for problems when necessary, watched some YT from other language solutions and was able to compare Rust to other languages in this way. There are quite a few of these types of exercises online and help getting your feet wet.

I have been using PowerShell for automation on Windows and a few months ago I started learning/using some of the well-known Rust libraries to replace some of these tasks (file parsing, REST processing, etc..). There are many quality libraries out there. Using these libraries and looking at their docs and source code, including std, really helps to understand how things work and more idiomatic Rust.

The interesting thing is that I find myself using Rust like a scripting language more and more - unwraps and ? everywhere!! I don't feel it's any slower than using $DYNAMIC_SCRIPTING_LANG and panics in exactly the same way. It's a really quick way to create useful code and If I want to build on top of those 'scripts' later, I can come back, add error handling, refactor parts into traits, structs, enums, etc.. and make it in to a real lib and/or bin. I find this is a good way to do useful things quickly and then improve them later as needed and you yourself improve with Rust.

Everyones journey is going to be different. Good Luck on yours!!

1 Like

I honestly think that in the general case this is a really heavy burden to shoulder while learning a language, and especially a language with a well-deserved reputation for difficulty. I emphasise 'in the general case' because, well, mutatis mutandis and all that. I'm sure there are people who because of their own particular circumstances (of ability or background or motivation or peer-availability) this approach would suit.

I think I could imagine, just about, doing what you suggest with Go. But the thing is with Rust, it's more than the challenge of the language, it's that the difficulty is viral, spreading to every library you'll use. I've been learning Rust for a few months, and though I'm probably of below-average ability, it feels like it would have half-killed me to have to produce. In my first actual Rust project -- an extremely simple cli app -- I have quite literally avoided adding features because I tried and failed to find a way to parse the commandline in the way the feature needed, using the most common library. And I spent a couple of days recently just exploring SQL libraries before I found something I could imagine using that would not become a multi-week learning project in itself.

There's definitely a kind of clockwork beauty to the precision with which everything snaps together when things work well in Rust, which I am sure (in addition to its objective merits) makes it worth the effort. But if you're finding it hard or frustrating to learn while needing to use it for what sounds like a hard enough ask in itself, that's hardly surprising. Perhaps you could modify your strategy - build your POC in something your know better, while learning Rust in the background, with a view to a rewrite? Or pick off some small performance/safety critical part of the app do do in Rust?

1 Like

I tend to think the best way to really learn a programming language is to write a real program in it. Actually several real programs, of increasing complexity. Rust's documentation is great, but it's only going to take you so far.

Now, those learning projects can be little throwaway personal projects. They probably should be, actually.

1 Like

I'm still learning Rust and stumbling at many things. It took 1 month to write at least something working, another month to "get it going" and convert a big and slow Python script into a working Rust binary. But still as I tried writing something harder, it would bite.

What I noticed is my tendency to try building a complex system right away, as I'm used to in Python -- because in it, I understand how things will connect and how they won't. In Rust, we don't have such feeling, and some minor details also add some friction.

But as long as I created things in the simplest possible way -- or just 1 notch more complex -- it did work, I'd get things done and working.

So, the recipe is to lower the ambitions and keep things simple.

What motivates me to go on rather than just keep riding the wave of Python is that the few programs that I converted from Python into Rust, never crashed. Whereas, in Python I had to run those heavy computations (like 1 or 4 hours long) in fail-to-debug-console mode for weeks before there'd be no crashed because of edge cases in the GIS data. So, all this pain with Rust compiler will pay off.

1 Like

Indeed.

In Rust, writing client code is trivially easy compared to writing library code or other kinds of code whose purpose is to be reused in multiple situations.

Writing reusable code in any language is more difficult than writing client code in the same language, but in Rust the difference is much bigger. For example: in Rust, in order to write generic code, you have to explain exactly what is going on with type bounds, and you have to think carefully about what you mean and consider all sorts of pitfalls that you might encounter and get all the details right, before the compiler is satisfied. When someone then tries to use this code, all those details have already been analysed by the original-author-and-compiler collaboration. Even if the human half of this collaboration has since been run over by a bus (or is otherwise unavailable for consultation), the compiler is still there, ready to explain all these requirements to the client code's author. Compare this to C++ templates, where the generic code's author just has to slap together some templates which look roughly OK, and the client code's author gets to solve any less obvious problems later on. (I've been trying to avoid C++, so I'm not up to speed with concepts, and don't know to what extent they help with this. But experience tells me that any C++ language design 'solution' brings with it a whole bunch of new headaches.)

Dynamically-typed languages are supposedly more productive for small projects and in the short term; statically typed ones are supposedly more productive for larger projects and in the long term. In my personal experience, these two classes of language are represented most strongly by Python and C++. I struggle to think of an occasion on which I was programming in Python and thought, "I wish I were writing this in C++ as that would be more productive", but I can think of a myriad of occasions on which the reverse was true.

As a project grows in size and complexity, there is supposed to be a point where it becomes less productive to be working in a dynamic language, and more productive to be working in a static one. In almost a quarter of a century of using these languages side-by-side, I have never reached this point along the Python-C++ axis.

Coming back to the original point: even though I'm no Rust maven, I only need to write about 50 lines of Python before I start wishing I were writing in Rust, because it would be more productive.

But this is only true if I'm writing client code. Writing code that is meant to be reused by other code, is still overwhelmingly easier in Python. But much of this will be because Python allows me to write code full of problems that the code's subsequent user will have to solve later, while Rust forces me to think about it up front.

TLDR

Writing application code in Rust is pretty easy; writing libraries and reusable code is much more difficult.

If you want to create applications rather than libraries, you'll need much less experience in Rust than you might suspect.

1 Like

I'm a bit surprised. I had the same issue and among others, but to me the reason initially (when I was a teenager) was just not enough proficiency in English -- there'd always be some unknown words or terms, and it would take several minutes and a big paper dictionary to understand the message.

To me it was a big moment when I started carefully reading them -- then I could complete things I'd be unable earlier. So when I later taught other people, I'd let them get errors and then insisted they'd read the messages, rather than jump into fixing what they supposed was wrong (or in some cases stubbornly trying to just run it again).

Don't worry, experienced programmers take them as small irritations, and know what to do:

try:
    your code
except:
    pass