Rust Is a Scalable Language

In my last post about Zig and Rust, I mentioned that Rust is a scalable language. Let me expand on this a bit.

Vertical Scalability

Rust is vertically scalable, in that you can write all kinds of software in it. You can write an advanced zero-alloc image compression library, build a web server exposing the library to the world as an HTTP SAAS, and cobble together a script for building, testing, and deploying it to wherever people deploy software these days. And you would only need Rust while it excels in the lowest half of the stack, its pretty ok everywhere else too.

Horizontal Scalability

Rust is horizontally scalable, in that you can easily parallelize development of large software artifacts across many people and teams. Rust itself moves with a breakneck speed, which is surprising for such a loosely coordinated and chronically understaffed open source project of this scale. The relatively small community managed to put together a comprehensive ecosystem of composable high-quality crates on a short notice. Rust is so easy to compose reliably that even the stdlib itself does not shy from pulling dependencies from crates.io.

Steve Klabnik wrote about Rusts Golden Rule, how function signatures are mandatory and authoritative and explicitly define the interface both for the callers of the function and for the functions body. This thinking extends to other parts of the language.

My second most favorite feature of Rust (after safety) is its module system. It has first-class support for the concept of a library. A library is called a crate and is a tree of modules, a unit of compilation, and a principle visibility boundary. Modules can contain circular dependencies, but libraries always form a directed acyclic graph. Theres no global namespace of symbols libraries are anonymous, names only appear on dependency edges between two libraries, and are local to the downstream crate.

The benefits of this core compilation model are then greatly amplified by Cargo, which is not a generalized task runner, but rather a rigid specification for what is a package of Rust code:

  • a (library) crate,
  • a manifest, which defines dependencies between packages in a declarative way, using semver,
  • an ecosystem-wide agreement on the semantics of dependency specification, and accompanied dependency resolution algorithm.

Crucially, theres absolutely no way in Cargo to control the actual build process. The build.rs file can be used to provide extra runtime inputs, but its cargo who calls rustc.

Again, Cargo defines a rigid interface for a reusable piece of Rust code. Both producers and consumers must abide by these rules, there is no way around them. As a reward, they get a super-power of working together by working apart. I dont need to ping dtolnay in Slack when I want to use serde-json because we implicitly pre-agreed to a shared golden rule.