First explore, then nail it down

World map drawn from polygons
Photo by Marjan Blan | @marjanblan / Unsplash

I've been having a bit of trouble writing Rust recently. I've been second guessing myself a lot about whether choosing it for a side project was a good idea. The reason for that is that I'm creating something that I've never done before, and generally speaking, I don't yet know what I'm doing (as it happens in the beginning whenever we try something new).

Rust is not very good at this, or so we may think. Here's the thing - people pushing evangelizing Rust do so by pointing out the absolute safety you can achieve with it. Or the most beautiful async code, or error handling, or program flow, or performance.

And it can do that. And it does do that.

The problem is if we understand this as "this is the only way to write Rust code", and immediately try to write our magnum opus when starting to work on something new. But as Rome wasn't built in a day, so will we also struggle in the beginning. Not only because there are a truckload of rules you have to adhere to when writing Rust (otherwise the compiler and clippy will gently push you towards knowing better), but also because you have to first figure out what you're building, and if that's what you want to build!

And therein lies the problem. Of course, you can write perfect code for a problem you don't yet know how to solve, but it will be hard.

When you're typing some idea out for the first time, you usually don't know what it will eventually take to get right. You may have incorrect assumptions about the problem domain, or the language, or the concurrency model, or all of them.

If you just spent hours coming up with the best abstractions, the best control flow to go with it, and exceptionally descriptive errors and error handling, only to find out that it's actually unusable because you haven't thought about how you need to put stuff into the database, then that's hours wasted.

So what I learned is to first get it done, then get it right. Feel free to use .unwrap() and .clone() as if they were free. Use anyhow and make every error return anyhow! errors. Don't worry about panicing.

Explore the problem domain. Find working solutions.

Only after you have something that works, then refactor. Use Rust's advantages to your benefit. It has all the necessary tools and safeguards that allow you to refactor somewhat fearlessly. You may break things, but not in a horrible you'll-find-out-in-four-months-on-production way.

At this time, you should have a solution that works. Now you can replace the unwrap() calls with proper error propagation. Or see if you can replace the .clone() calls with references, or some other kind of magic.

It's okay to start with "quick and dirty" code.

P.s.: This of course isn't unique to Rust, as you can fall into the same pit with most other strongly typed (and even dynamic) languages. It's just that I feel it exceptionally with Rust.