Fundamentals 14 min read

Why Rust Might Be the Best Choice for WebAssembly – Pros, Cons, and Real‑World Lessons

After three years of building the Wick framework with Rust and WebAssembly, the author shares practical insights on Rust's strengths—such as its borrow checker, type system, and Clippy linting—and its drawbacks, including tooling gaps, async friction, and refactoring challenges, to help developers decide if Rust is worth the investment.

Architecture Development Notes
Architecture Development Notes
Architecture Development Notes
Why Rust Might Be the Best Choice for WebAssembly – Pros, Cons, and Real‑World Lessons

Several years ago I dropped everything to dive into WebAssembly, and Rust offered the most complete support and the best runtime ecosystem, so I jumped in eager to see what the hype was about.

Since then I, together with some great collaborators, built Wick, an application framework and runtime that uses WebAssembly as its core module system.

After three years, many production deployments, an e‑book, and about a hundred crates on crates.io, I feel it’s time to share some thoughts on Rust.

Pros

Maintain More with Less Money

I’m a strong advocate of test‑driven development. Writing tests in Rust feels different because the compiler catches so many errors that many test cases become unnecessary. By avoiding unsafe blocks and .unwrap() , you start from a baseline that prevents many problems by default.

The borrow checker, rich type system, pattern‑matching, extensive libraries, and the absence of null values let me maintain over 70,000 lines of code in Wick with far fewer tests than I would need in other languages.

When you need to add a test, Rust’s built‑in test framework lets you do it next to the code with virtually no extra effort.

I Can Write Better Code in Other Languages Now

Programming in Rust feels like an emotionally abusive relationship – the compiler constantly yells at you about things you consider normal elsewhere. Over time you get used to its tantrums, learn to walk the tightrope, and the habits stick.

Although emotional abuse isn’t a healthy way to encourage change, it does produce change.

Now, when I write code in other languages, I feel uneasy if the code order is chaotic or return values are unchecked, and I get a similar discomfort when runtime errors appear.

Clippy Is Awesome!

Clippy is Rust’s linter, but calling it a linter feels rude; in a language that can make you cry, Clippy acts more like a gentle friend.

The standard library is massive, and many useful functions are scattered across fine‑grained types, traits, macros, and functions, making discovery hard. Clippy rules (e.g., manual_is_ascii_check ) suggest more idiomatic replacements for common patterns.

With hundreds of rules targeting performance, readability, and unnecessary indirection, Clippy often offers ready‑made code replacements.

It finally feels possible to configure a global lint for a project; previously we had to hack together solutions to keep linting consistent across dozens of crates in Wick.

Cons

Some Gaps You Have to Tolerate

When revisiting Clippy issues, I keep doubting my sanity, fearing I missed a configuration. Those problems have existed for years and are only now being resolved.

Clippy is great, but this use case is just one of many in the Rust ecosystem. I often encounter libraries or tools that don’t cover my scenario, which is common for newer languages or projects, though Rust isn’t exactly new.

Early adopters and new users in open source tend to solve extreme cases, and their PRs improve the project for the next user. Rust has been voted “most loved language” for most of the past decade, attracting users but not always delivering significantly improved libraries or tools.

I suspect the pressure to maintain a stable API and Rust’s fine‑grained type system make it hard for library owners to iterate without causing major version bumps.

Or perhaps writing Rust code that does everything for everyone is extremely difficult, and people shy away from that effort.

Cargo, crates.io, and Project Structuring

I modeled Wick’s repository after other popular projects; it worked well until publishing issues surfaced.

While Cargo makes building, testing, and using modular packages easy, publishing to crates.io is another story.

You can’t publish a package unless every referenced crate is also published individually, which makes sense—you don’t want dependencies that exist only on a local file system.

Many developers naturally split large projects into smaller modules, but you can’t publish a parent crate that contains sub‑crates existing only within itself, nor can you publish crates with local development dependencies. This limitation feels arbitrary and unnecessary.

Fortunately, Cargo’s workspace support is excellent, offering a better experience for managing large projects than most languages, though it doesn’t solve the deployment problem. You can set up a workspace in many ways, but none are straightforward to deploy.

The ecosystem provides numerous utility crates to simplify workspace publishing, each targeting a specific configuration, yet the “one true way” to set up a workspace still confuses me. Publishing Wick usually took over an hour, combining manual, repetitive tasks with partially working tools.

Async

Rust added async after the language was born, feeling like an afterthought that often introduces hard‑to‑understand errors. When searching for solutions, you must filter by runtime and async style, and you may be unable to use an async library outside its specific runtime.

After two decades of JavaScript and Go experience, async is the biggest source of friction in Rust. It’s not an insurmountable problem, but when the async monster appears you must be ready to tackle it, whereas in other languages async is almost invisible.

Ugly

Refactoring Can Be Hard

Rust’s rich type system is both a blessing and a curse. Thinking in Rust types feels like dreaming; managing them can be a nightmare. Your data and function signatures may involve generic types, lifetimes, and trait bounds, which can multiply.

You also need to define all generic impl s for each type. The first write is tedious, but refactoring can turn a small change into a tangled mess.

When you have to adjust fourteen different definitions just to move forward, progress becomes painfully slow.

The burden isn’t expressiveness; it’s the lack of language or tooling solutions to reduce repetition. You often need the same constraints or refer to the same generic list, but there’s no aliasing or central reference mechanism.

Verdict

I love Rust. I love its capabilities and versatility; I can write system‑level code for CLI tools, web servers, and web clients with the same language. Using WebAssembly, I can run the exact same binary in the browser to power LLMs, which still amazes me.

I also love how rock‑solid Rust programs feel. Once you appreciate the protection Rust offers, it’s hard to go back to other languages. I briefly returned to Go, enjoyed the speed, but then hit runtime panics that shattered my confidence.

Rust does have downsides: it’s hard to hire, learning is slow, it can be overly rigid, async code is tricky, not all libraries are equally safe, and tooling has many gaps. You start behind and face many obstacles, but if you overcome them you can leave others in the dust—a big assumption.

Is Rust worth it for you? If you need rapid iteration, maybe not. If you have a well‑defined scope and can afford higher upfront costs, consider it. You’ll end up with rock‑solid software, and as WebAssembly matures, writing once and reusing everywhere becomes a realistic prospect.

RustWebAssemblyAsyncCargoClippyWick framework
Architecture Development Notes
Written by

Architecture Development Notes

Focused on architecture design, technology trend analysis, and practical development experience sharing.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.