Hey Rustaceans! Python dev here, diving into Rust and would love some advice

Hey everyone,

Total Rust newbie here, hoping to find my footing. My main background is in Python (mostly for AI/ML stuff in college), and after a bit of a back and forth on whether to learn Java or Rust next, I've decided to jump headfirst into the world of Rust!

Honestly, I'm just super drawn to Rust's raw power, the whole memory safety promise, and seeing it pop up everywhere from system tools to WebAssembly. I'm really looking to build a deeper, more fundamental understanding of how software works, and Rust feels like the right place to do that.

Now, I'm not going in blind I've definitely been warned about the three big bosses: ownership, the borrow checker, and lifetimes. Coming from Python where the computer just handles all that stuff for you, I know I'm in for a bit of a mental workout, but I'm ready for it!

That's where you all come in! I'd be so grateful for any pointers you could give a beginner. I'm trying to figure out:

  • Best Learning Resources: I've already started "The Book," but what are some other gems? Any videos, blogs, or tutorials that really made the tough concepts finally "click" for you?

  • Common Stumbling Blocks: What are the classic mistakes people from my kind of background (Python/Java) usually make when they're starting out?

  • Making Peace with the Borrow Checker: I've heard you have to learn to work with the borrow checker, not fight it. Any tips or analogies that helped you finally get it?

  • First Project Ideas: What's a good "Hello, World!" plus one project? Something small that really forces you to grapple with the core ideas.

Super excited to get started and become part of this community. Thanks a ton for any pointers you can throw my way!

Cheers!

They try to apply their past programming experience to Rust. But names “ownership”, “borrow” and “lifetimes” try to bring entirely different intuition, much older one.

Imagine that your objects are read-world, physical, objects. One of the best analogues is textbook/workbook analogue: textbook can be read together (you can find lots of cure pictures with "girl and boy share the textbook") while workbooks are one per pupil.

And last, but not least, don't forget the emphasis: &T is “shared, usually read only reference” while &mut T is “always unqiue and thus read-write reference”. Read this blog post, maybe.

1 Like

To me something more important than avoiding fights with the borrow checker is to avoid fighting the idea of fighting the borrow checker. Because fighting it and diving deep into the compiler error messages is the best way to learn about how Rust’s ownership and borrowing model works.

As for what problems does the borrow checker solves which is the important part; I found learning embedded C as a side-quest, especially gnarly bits about proper and safe pointers and allocated memory management, helped me a lot to understand the true value of it.

Documents and resources from the MISRA consortium, which is dedicated to embedded C safety and also how an entire industry is revolving around keeping C code safe can also give an idea: MISRA C - Wikipedia

4 Likes

Take a look at burn:

% git clone https://github.com/tracel-ai/burn.git

% cargo run --release --example
error: "--example" takes one argument.
Available examples:
    ag-news-infer
    ag-news-train
    custom-csv-dataset
    custom-cubecl-kernel
    custom-image-dataset
    custom-renderer
    custom-training-loop
    custom-wgpu-kernel
    db-pedia-infer
    db-pedia-train
    guide
    hf_dataset
    lstm-infer
    lstm-train
    mnist
    named-tensor
    regression
    server
    speech_commands
    text-generation
    wgan-generate
    wgan-mnist

I'm surprised no one has commented on the style of your first post, but never mind, welcome to rust :rocket:

1 Like

For me, the best books to start learning Rust:

A lot of websites can be found from the rust-lang.org website, but here are a few off the top of my head:

Since you're coming from Python and Java-ish, I'd say the most common problem is trying to map OOP patterns to Rust, which doesn't have all the OOP features but offers its own system (e.g. enum, traits). It's a process, so at first you'll probably be tempted to "abuse" the language a little to find an equivalent, but after a while, you should get a more idiomatic approach. Of course, your mileage may vary.

Also, there's no garbage collector, so it's conceptually quite different. In Python and Java, you can happily create an object anywhere and almost unconsciously replicate references to it, from which you can modify and use the object without restrictions.

In Rust, You must get used to the idea that one piece of data is either 1) shared non-exclusively ("read-only" pointer), 2) shared exclusively (r/w pointer), or 3) moved to another owner.

The day I'll be at peace with it is the day I move on to another language. :wink:

I'd venture that it depends on your style. You can carefully analyze your variable and the lifetime of your data, but you're bound to have to fight it now and then, at least at the beginning.

The compiler gives very helpful hints most of the time: take the time to read them and understand what it tries to tell you. Sometimes, you'll have to modify your algorithm or split your data to achieve what you want. I don't really have a unique solution in mind that I can give you, unfortunately.

I'm not sure. Ideas usually popped in my head after reading a chapter or learning a new concept, but we all have our own way to learn.

Do little projects that appeal to you but aren't too complicated at first. I'm sometimes tempted to try things a little too difficult for my level, then I realize I'm cherry-picking parts that I can't master yet, and it's a messy way to learn. Avoid that. :slight_smile:

Hope you'll have fun in your journey!

4 Likes

Here's a list of links I keep for resources I like:

Many of the resources were mentioned by redglyph above, but I also link to a lot of other things too.

1 Like

I have saved several useful learning resources for the last couple of years as well. Hope they are useful for you: Firebits - Your online hub for building and sharing knowledge

2 Likes

Coming from Python, I think the most important thing is to make sure that you don't fall into the trap of thinking that a compiler error is some kind of "failing" on your part. It's not at all.

Read the errors carefully. Feel free to think of them like test failures in TTD: a normal part of having the tools help you do what you were trying to do. And make sure you see the whole multi-line error. Rust often gives useful context, help, and suggestions, so make sure you're not just getting it all smushed into one unreadable line in an IDE list.

5 Likes

I wish I knew about these things earlier:

Packages are really good by default, definitely look for something existing much more often than you might otherwise in another ecosystem. Try lib.rs as an alternative UI to crates.io - it has a lot of categories to explore to find things easier, and it is a bit better to search in my experience.

In particular, error handling can seem really annoying until you find anyhow and thiserror (and their alternatives), which are for application / untyped errors and library / typed errors respectively. Essential is a bit strong, but a great default.

Make sure you're using rustfmt and clippy! They are probably already installed and maybe even enabled in your editor by default, but it's good to be sure.

There’s a few big rules of thumb to avoid the borrow checker:

Don't add lifetime parameters to types (Foo<'bar> for example) - you might eventually get to a use case for them but if you don't absolutely know for sure it's what you want, you almost certainly should be cloning something instead.

In general, the fix for a borrow error is to clone something! It's generally not a performance problem to copy something, it's explicit at least partially because it has a behavioral difference. If you do have a large amount of data you want to share, you may want to use Rc (or Arc) so the clone is only incrementing a reference count, but be aware that makes the shared value read only by default.

If you need to modify a value, before making a reference a mutable reference, consider whether you actually want "interior mutability" - if you actually know the code in question is only ever going to be the only code with access to the value it's fine to use a mutable reference, but otherwise you may want to instead wrap the value in something like Cell or it's related types.

At a higher level, borrow checking often is much simplified when you separate determining what changes to make from applying the change. This can be in "time", for example adding to a list of items to modify in a loop, then applying the modifications in a loop; or in "space", for example always creating a new list of items, simply cloning the old item if there's no change. This is something that takes the longest to build "muscle memory" around until it feels natural, in particular there's a lot of "but that's inefficient" instincts that you need to actually check before assuming, that are often wrong on modern machines.

Probably a lot more I'm forgetting, but that should be good for now!

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.