Choosing a Rust web framework, 2020 edition

July 2020 · 11 minute read

This post was originally meant as a section of Zero To Production to explain the reasoning behind our technology choice. It eventually grew so large to be its own article!

You can discuss the article on HackerNews or r/rust.

As of July 2020, the main web frameworks in the Rust ecosystem are:

Which one should you pick if you are about to start building a new production-ready API in Rust?

I will break down where each of those web frameworks stands when it comes to:

I will in the end make my recommendation.
Worth remarking that there are no absolutes: different circumstances (and taste) might lead you to a different pick.

1. Comprehensiveness

actix-web, tide and warp are slim web frameworks: they offer you an HTTP web server, routing logic, middleware infrastructure and basic building blocks and abstractions to parse, manipulate and respond to HTTP requests.

rocket takes a different approach - it aims to be batteries-included: the most common needs should be covered by functionality provided out-of-the-box by rocket itself, with hooks for you to extend rocket if your usecase needs it.
It should not come as a surprise then that rocket ships an easy-to-use integration to manage connection pools for several popular database (e.g. Postgres, Redis, Memcache, etc.) as well as its own configuration system in rocket-contrib, an ancillary crate hosted in rocket’s own repository.

We can compare them to frameworks available in other ecosystems:

Of course this is a snapshot of the landscape as of today, but the situation is continuously shifting according to the maintainers’ intentions - e.g. actix-web has slowly been accumulating more and more supporting functionality (from security to session management) in actix-extras, under the umbrella of the actix GitHub organization.
Furthermore, using a slim web framework does not force you to write everything from scratch as soon as the framework is falling short of your needs: you can leverage the ecosystem built by the community around it to avoid re-inventing the wheel on every single project.

2. Community and adoption

Numbers can be misleading, but they are a good conversation starting point. Looking at crates.io, we have:

Framework Total Downloads Daily Downloads
actix-web ~1250k ~3000
rocket ~525k ~1000
warp ~435k ~3000
tide ~47k ~300

The number of total downloads is obviously influenced by how long a framework has been around (e.g. actix-web:0.1.0 came out at the end of 2017!) while daily downloads are a good gauge for the current level of interest around it.

You should care about adoption and community size for a couple of reasons:

The second point is particularly important for slim frameworks.
You can get a feel of the impact of community size, once again, by looking at the number of results popping up on crates.io when searching a framework name:

Framework # results
rocket 178
actix-web 113
warp 57
tide 20

Will all those crates be relevant? Unlikely.
Will a fair share of them be outdated or unproven? Definitely.

Nonetheless it is a good idea, before starting a project, to have a quick look for functionality you know for a fact you will need. Let’s make a couple of quick examples with features we will be relying on in the email newsletter implementation we are building in Zero To Production:

Most of these features are not too much work to implement, but the effort (especially maintenance) compounds over time. You need to choose your framework with your eyes wide open on the level of commitment it is going to require.

3. Sync vs Async

Rust landed its async/await syntax in version 1.39 - a game changer in terms of ergonomics for asynchronous programming.
It took some time for the whole Rust ecosystem to catch up and adopt it, but it’s fair to say that crates dealing with IO-bound workloads are now generally expected to be async-first (e.g. reqwest).

What about web frameworks?
actix-web adopted async/await with its 0.2.x release, same as warp, while tide was using async/await before its stabilisation relying on the nightly Rust compiler.
rocket, instead, still exposes a synchronous interface. async/await support is expected as part of its next 0.5 release, in the making since last summer.

Should you rule out rocket as a viable option because it does not yet support asynchronous programming?
It depends.
If you are implementing an application to handle high volumes of traffic with strict performance requirements it might be better to opt for an async web framework.
If that is not the case, the lack of async support in rocket should not be one of your primary concerns.

3.1. Futures runtime

async/await is not all sunshine and roses.
Asynchronous programming in Rust is built on top of the Future trait: a future exposes a poll method which has to be called to allow the future to make progress. You can think of Rust’s futures as lazy: unless polled, there is no guarantee that they will execute to completion.
This is often been described as a pull model compared to the push model adopted by other languages1, which has some interesting implications when it comes to performance and task cancellation.

Wait a moment though - if futures are lazy and Rust does not ship a runtime in its standard library, who is in charge to call the poll method?
BYOR - Bring Your Own Runtime!
The async runtime is literally a dependency of your project, brought in as a crate.
This provides you with a great deal of flexibility: you could indeed implement your own runtime optimised to cater for the specific requirements of your usecase (see the Fuchsia project or bastion’s actor framework) or simply choose the most suitable on a case-by-case basis according to the needs of your application.
That sounds amazing on paper, but reality is a bit less glamorous: interoperability between runtimes is quite poor at the moment; mixing runtimes can be painful, often causing issues that are not straight-forward either to triage, detect or solve.
While most libraries should not depend on runtimes directly, relying instead on the interfaces exposed by the futures crate, this is often not the case due to historical baggage (e.g. tokio was for a long time the only available runtime in the ecosystem), practical needs (e.g. a framework has to be able to spawn tasks) or lack of standardisation (e.g. the ongoing discussion on the AsyncRead/AsyncWrite traits - see here and here).
Therefore picking an async web framework goes beyond the framework itself: you are choosing an ecosystem of crates, suddenly making it much more cumbersome to consume libraries relying on a different async runtime.

The current state of affairs is far from ideal, but if you are writing async Rust today I’d recommend you to make a deliberate choice when it comes to your async runtime.

The two main general-purpose async runtimes currently available in Rust are tokio and async-std.
tokio has been around for quite some time and it has seen extensive production usage. It is fairly tunable, although this results in a larger and more complex API surface.
async-std was released almost a year ago, around the time of async/await stabilization. It provides great ergonomics, while leaving less room for configuration knobs.

crates.io can once again be used as a gauge for adoption and readiness:

Runtime Total Downloads Daily Downloads
tokio ~9600k ~30k
async-std ~600k ~4k

How do frameworks map to runtimes?

Framework Runtime
actix-web tokio
rocket (0.5.x) tokio
tide async-std
warp tokio

4. Documentation, tutorials and examples

Having to dive into the source code to understand how something works can be fun (and educational!), but it should be a choice, not a necessity.
In most situations I’d rather rely on the framework being well-documented, including non-trivial examples of relevant usage patterns.
Good documentation, tutorials and fully-featured examples are mission-critical if you are working as part of a team, especially if one or more teammates are not experienced Rust developers.

Rust’s tooling treats documentation as a first class concept (just run cargo doc --open to get auto-generated docs for your project!) and it grew to be part of the culture of the Rust community itself. Library authors generally take it seriously and web frameworks are no exception to the general tendency: what you can find on docs.rs is quite thorough, with contextual examples where needed.
rocket and actix-web provide high-level guides on the respective websites and all frameworks maintain a rich collection of examples as part of their codebases2.

Tutorials outside of the project documentation are mostly a function of age: it’s very easy to find material (articles, talks, workshops) on actix-web and rocket while the offering is somewhat more limited for warp and tide. On the flip side, some of what is out there for actix-web and rocket might target older versions, leaving room for confusion.

5. API and ergonomics

Well, difficult to give an opinion on API design that sounds legitimately objective.
We all have wildly different tastes when it comes to what we consider a pleasant API and there is no substitute for a quick hack-and-go to really get a feel for what it is like to use a certain web framework.

If you are short on time, you can have a look at worked out examples: actix-web’s examples, warp’s examples, tide’s examples and rocket’s examples.

If you are curious about warp and tide, Image decay as a service provides an in-depth analysis of their APIs.

6. Our choice

As of July 2020, I’d suggest picking actix-web if you are writing a production API in Rust.

To recap what we covered, actix-web:

While some of its APIs are definitely not the most ergonomic (I am looking at you, Transform trait), the inconvenience is definitely minor all things considered.

On the flip side, Rust itself would not be where it is today if nobody had been willing to take a bet on a promising but less proven technology:

A rising tide lifts all boats.

The way of saying from which tide takes its name, the way forward for the whole Rust async ecosystem.
See you again in a year for another overview!

Thanks to o0Ignition0o and vertexclique for taking the time to review the draft of this article.


If you want to be notified when new articles are released on this blog, subscribe to the email newsletter.


  1. Check out the release notes of async/await for more details. The talk by withoutboats at Rust LATAM 2019 is another excellent reference on the topic. If you prefer books to talks, check out Futures Explained in 200 Lines of Rust.
    [return]
  2. actix-web’s examples, warp’s examples, tide’s examples and rocket’s examples.
    [return]