A few years ago, I dropped everything to focus 100% on WebAssembly. At the time, Rust had the best support for compiling into WebAssembly, and the most full-featured WebAssembly runtimes were Rust-based. Rust was the best option on the menu. I jumped in, eager to see what all the hype was about.
Since then, I (along with some other awesome people) built Wick, an application framework and runtime that uses WebAssembly as its core module system.
After three years, multiple production deployments, an ebook, and ~100 packages deployed on crates.io, I feel it’s time to share some thoughts on Rust.
You can maintain more with less
I am a massive proponent of test-driven development. I got used to testing in languages like Java and JavaScript. I started writing tests in Rust as I would in any other language but found that I was writing tests that couldn’t fail. Once you get to the point where your tests can run – that is, where your Rust code compiles – Rust has accounted for so many errors that many common test cases become irrelevant. If you avoid unsafe {}
blocks and panic-prone methods like .unwrap()
, you start with a foundation that sidesteps many problems by default.
The aggressiveness of Rust’s borrow checker, the richness of Rust’s type system, the functional patterns and libraries, and the lack of “null” values all lead to maintaining more with less effort spent in places like testing. I’ve maintained the 70,000+ lines of code in the Wick project with far fewer tests than I would need in other languages.
When you need to write tests, adding them on the fly is easy without thinking about it. Rust’s integrated test harness lets you add tests right next to code with barely a second thought.
I code better in other languages now
Programming in Rust is like being in an emotionally abusive relationship. Rust screams at you all day, every day, often about things that you would have considered perfectly normal in another life. Eventually, you get used to the tantrums. They become routine. You learn to walk the tightrope to avoid triggering the compiler’s temper. And just like in real life, those behavior changes stick with you forever.
Emotional abuse is not generally considered a healthy way to encourage change, but it does effect change nonetheless.
I can’t write code in other languages without feeling uncomfortable when lines are out of order or when return values are unchecked. I also now get irrationally upset when I experience a runtime error.
done"
is not a function? Why didn’t you let me know “done”
might not be a function??Clippy is great!
Clippy is Rust’s linter, but calling it a linter is a disservice. In a language where the compiler can make you cry, Clippy is more of a gentle friend than a linter.
The Rust standard library is enormous. It’s hard to find functions you know probably exist when so much functionality is spread across myriad granular types, traits, macros, and functions. Many Clippy rules (e.g., manual_is_ascii_check
) look for common patterns that stdlib methods or types would better replace.
Clippy has hundreds of rules that tackle performance, readability, and unnecessary indirection. It will frequently give you the replacement code when possible.
It also looks like (soon) you’ll finally be able to configure global lints for a project. Until now, you had to hack your solution to keep lints consistent for projects. In Wick, we use a script to automatically update inline lint configurations for a few dozen crates. It took years for the Rust community to land on a solution for this, which brings us to…
There are gaps that you’ll have to live with
I questioned my sanity every time I circled back around to the Clippy issue above. Surely, I was wrong. There