Part of the “Rustober” series.
A web service in Rust is much more than it seems. Whereas in Go you can just srv := &http.Server{}
and srv.ListenAndServe()
, in Rust you have to bring your own async runtime. I remember when async was very new in Rust, and apparently since then the project leadership never settled on a specific runtime. Now you can pick and choose (good), but also you have to pick and choose (not so good).
In practical, real-world terms it means you use Tokio. And tower
. And hyper
. And axum
. And sqlx
. I think that’s it? My default intention is to use as few dependencies as possible — preferably none. For Typingvania I wrote the engine from scratch in C and the only library I ship with the game is SDL. DDPub that runs this website only really needs Markdown support. However, with Rust I want to try something different and commit to the existing ecosystem. I expect there will be plenty of challenges as it is.
So how does all of the above relate to each other and why is it necessary?
┌──────────────────────────┐
│ axum (web framework) │
└────────┬────────────┬────┘
│ │
┌────▼───┐ ┌───▼────────────────┐
│ hyper │ │ tower (middleware) │
└────┬───┘ └───┬────────────────┘
│ │
└────────┬───┘
│
┌─────▼─────┐
│ tokio │
└───────────┘
In short, as far as I understand it now:
tokio
, being the source (all three of the others are subprojects) provides the runtime I mentioned above and defines how everything else on top of it works.tower
provides theService
trait that defines how data flows around, with abstractions and utilities around it, but is protocol-agnostic. Here’s the trait:
// An asynchronous function from a Request to a Response.
pub trait Service<Request> {
type Response;
type Error;
type Future: Future<Output = Result<Self::Response, Self::Error>>;
fn poll_ready(
&mut self,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>>;
fn call(&mut self, req: Request) -> Self::Future;
}
hyper
implements HTTP on top oftower
’sService
trait, but as a building block, not as a framework.axum
is a framework for building web applications — an abstraction overtower
andhyper
. Effectively, I will be using mostlyaxum
facilities implemented using the other three libraries.- Finally,
sqlx
is an asynchronous SQL toolkit usingtokio
directly.
Because all of the libraries are so integrated with each other, it enables (hopefully) a fairly ergonomic experience where I don’t have to painstakingly plug things together. They are all mature libraries tested in production, so even though it’s a lot of API surface, I consider it a worthwhile time investment: my project will work, and more likely than not I’ll have to work with them again if/when I get to write Rust in a commercial setting — or just work on more side projects.