Quick Advice to New Rustaceans

11 Jan 2025

Let’s say you’re new to Rust. Maybe you’ve read TRPL and worked through Rustlings or YARR. You can write enough Rust to solve problems, and can fix compiler errors when they crop up. Now you come to a more nebulous question: how do you write good Rust? And how do you write Rust that won’t require mortal combat with the borrow checker?

Start by letting go of a no-compromises approach to performance.

Don’t Be Afraid to Clone

Because Rust makes cloning explicit, I get the feeling that a lot of newcomers find it stressful. How expensive of an operation is a .clone()? Unfortunately, the answer can range from “very expensive” to “very cheap.” Reference-counted structures like an Arc or a channel can be cloned very cheaply. Generally strings like usernames, filenames, or paths are small, and therefore cheap. In these cases clone first and think later.

In cases where you’re uncertain, I would still strongly recommend cloning! You can always profile your code to see if the clones are performance drags, but I promise you it’s unlikely. In most cases choosing more suitable data structures, adding caches, or improving memory locality will all pay bigger dividends than agonizingly avoiding allocation.

Don’t Store References

The number one way new-to-Rust programmers make their lives harder is to deal with lifetime annotations. I have been writing Rust for a little under ten years; I have maintained a game library and written a hobby compiler. I still find code that makes heavy use of lifetime annotations confusing. How do you avoid them? Don’t store references in data structures. Instead, clone the values (if they can be cloned cheaply) or use an Arc or Cow as appropriate.

Use Send and Sync containers

Technically, Rc and RefCell are lighter-weight than Arc and Mutex. If you’re 100% sure that a given bit of code will never become concurrent, by all means use them. For example: audio code tends to run in a dedicated thread, so you know that there’s no reason to share that data. In any other case I strongly recommend going with the Send and Sync alternatives instead. You’ll be unlikely to notice the performance cost, and you’ll be grateful to not have to refactor swathes of your program if you want to introduce some kind of parallelism later.

(Bonus) Check out the Rust API Guidelines

They explain some key heuristics to writing good Rust. Here are the few entries that I think are both most important and least intuitive: