By Radosław Miernik · Published on · Comment on reddit
I remember that a couple of years back,
Today, with WebAssembly, we are a thousand steps closer to this goal. As it becomes more and more powerful, it makes sense to take other languages into consideration while working on a web application. Adobe announced that they managed to squeeze Photoshop into the browser – that’s huge!
I believe there’s no need for an introduction of Rust. According to the Stack Overflow’s Annual Developer Survey, it’s the most loved language for six years straight. As such, it gained an enormous community, including some passionate web developers. Some of them focus on rewriting everything in Rust1; others began mixing it more reasonably, under the hood, as an implementation detail.
How about your project?
Setting up a new project for a full-stack developer like me is a tedious task. Of course, I can use one of the existing boilerplate projects, but none of them ever felt right to me. There’s always a package (or twenty) that I wouldn’t use, two that are missing. And I’d have to adjust most of the configuration files anyway. But I do it only once in a while, so maintaining a template doesn’t really make sense – it’d get outdated by the time I’d need it again.
When it comes to Rust, basically everything I need is already included2. Need a code formatter?
cargo fmt. Linter?
cargo clippy. Test runner?
cargo test. Benchmark harness?
cargo bench. Documentation generator?
cargo doc. There are over 30
cargo commands in total. Of course, not all of them are necessarily unique, and they have some limits, but hey – they are official.
I was amazed when I first saw Go’s
go fmt, or Elixir’s
mix docs. You say that everyone is using it? Count me in! Rust, as (most) modern languages (including Nim), does the same. Yet, the tools it provides are of excellent quality3.
And last, but definitely not least, the compiler itself. Yes, it’s infamously slow (although it’s getting better with each version), but the resulting programs are fast. Also, compilation errors are very informative, although they require some practice (especially when it comes to lifetimes). Just look how helpful it is:
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison -/example.rs:4:28 | 4 | if node_count as usize < visited
I can imagine how different life of a C++ developer coming to Rust is when adding any external code is as simple as
cargo install – no more copying code by hand (or even worse: using
pip on a daily basis.
In terms of numbers, there are more than 75k crates on crates.io right now. It’s not that many if we compare it to npm (1.85m) or PyPI (352k), but it’s still way more than one would ever need. Quality-wise, I’d say it’s strictly better – most of them have documentation and test suites (of course, using the official tools).
The most popular packages include
rand (random number generation),
syn (Rust source code parser; it’s used for implementing advanced macros),
serde ((de)serialization framework), and
bitflags (bitset data structure macro). This sample shows that some features that we may consider standard are not part of the language or even the standard library.
The standard library itself is growing slowly but steadily. Basically every single release stabilizes new APIs. And if you’d like to try the non-stable ones, just switch to a beta or nightly release. Coming from Node.js, it’s much faster – new APIs are introduced on average once a month. The best part is that “quality of life” functions are added regularly (not like
I have to make one honorable mention here. For my master thesis, I spent a couple of days reimplementing the Nim engine for Legends of Code and Magic. Both engines were already fast, but I wanted to make the new one parallelized. In Nim, I used the
parallel section to magically parallelize the work. I looked for a similar thing in Rust, and I found
rayon. It took me literally one line to make the code automatically span across all of the available CPUs.
I do like functional programming and everything that is common in these languages – algebraic data types (with pattern matching), implicit returns,
if expressions (plus
if let and
while let), using
Options instead of
Result instead of
catch. I find it easier to read and understand. Rust is an imperative language, but it reads like a functional one (mostly).
If we combine it with the beforementioned tooling, excellent packages, and active development, we have everything that is needed for a perfect language. Additionally, the fact that we may use macros to extend the language pushes the boundaries even further. Do you like JSX? All you need is a single package. There’s no need for a separate compilation step or a compiler extension.
Macros, while powerful, are relatively hard (conceptually). However, the trait system is the single best feature of Rust to me. You can compare them to interfaces in TypeScript, but there’s a twist – you can implement a trait for all of the existing types. It drastically changes the way you think about the methods but is surprisingly handy4.
Also, do you like
await syntax for asynchronous code? You can do it in Rust too! What is more, the code is automatically verified for correctness. All data types must be explicitly marked as thread-safe, using
Sync traits (or both). That sounds like a tedious task, but it actually makes us think about multithreading sooner than ever.
It’s also important how easy integrating Rust with other languages is. There’s the
ffi for C-like compatibility,
wasm-pack for WebAssembly, and Neon for creating Node.js addons. With all of these, we can replace basically every web developer tool with Rust5. We can do it feature-by-feature!
The popularity of a language is not a major factor for me – I have successfully delivered a couple of projects in Elixir, Nim, and even Pony. The fact that Rust does have a large community is a great addition, though. Every single problem I have ever encountered has already been asked (and answered!) on Stack Overflow or resolved in a GitHub issue. There’s also a very active forum.
The popularity is not limited to programmers – there are a lot of (big) companies investing a lot of resources in the language: Cloudflare, Coursera, Discord, Dropbox, Figma… I strongly recommend them, as most have happy endings but also a couple of non-trivial problems on the road.
Unsurprisingly, it flooded my RSS feed for the last couple of years. Whether it’s yet another “X in Rust” post, a successful company story (like the ones above), or a new programming language – they are a significant part of my readings.
Rust is here to stay – that’s for sure. I’d say we’ll see more and more tools going the same path as Parcel, that is, some of their parts will be incrementally rewritten in Rust (or other languages). I believe we all will benefit from that, especially on the performance and stability side.
How much of the webdev tooling will be in Rust a year from now? We’ll see.
I just had to jokingly mention “Rewrite everything in Rust”. Jokes aside, there is an actual list of projects rewritten in Rust. If you are a frequent terminal user, you may find something for yourself. That’s how I found
I don’t consider myself a rustacean, but not a bad Rust programmer either. And yet, a few notes from
cargo clippy improved the performance of my code by 15%! To be exact, it suggested some non-trivial changes in the internal representation of the data that allowed me not to clone it that often.
It reminds me of the dual nature of methods in Nim. Basically every function that accepts an argument of type
T is its method. There are two method call syntaxes:
f(x, a, b, ...) or
x.f(a, b, ...) (with some limitations).
esbuild is a similar case but in Go.