Chris Morgan › Blog Tween: a middleware library experiment This article is intended for people who are familiar with how web frameworks such as Django and Iron work; if you’re not familiar with such things you probably won’t get much out of this particular post. I’m not going to be explaining everything you might need to know. Tagged: Web, Rust

Published: June 1, 2016

Personal update (for those interested) I’m steadily getting back into R&D for a Rust web framework. I’ve been mostly off the radar for most of the last year, but I haven’t left Rust! I’ve been using it in my spare time, I just haven’t had much of that to dedicate to it, and I haven’t been doing much research that would be directly useful to others. Now at last I am, so here it is. After this, one of the next things I’m intending to tackle is a templating language inspired by ASP.NET Razor—​or Play Framework’s Twirl, which is much the same thing for Scala. I like statically typing all the things and am not fond of the handlebars and mustache templating engines for that reason, however much I like my own moustache. Lots of nice correctness and performance wins to be had. We can talk about that at some point, and if anyone wants to help out there, it’d be nice—​I haven’t worked with rustc much before, this will end up a fairly heavy-weight compiler plugin with lots of parsing and all to do. Should be fun. One of these days it’ll get to the stage where I finally start making the web framework that I came to Rust in the first place a few years back to make, rather than just making components for it. I’m hoping we’ll have a viable compile-to-JS story by then so I can proceed to experiment with an end-to-end Rust web app.

The existing state of middleware libraries in Rust

I’ll use iron::middleware as an example of the current state of middleware in Rust:

The request and response objects have slots for adding data to them: Request.extensions and Response.extensions , which are TypeMap s;

and , which are s; Middleware can inject things into the request and/or response objects, or not, as the fancy takes you, and read from it to get data from previous middleware or the main handler in post-processing;

There is no type-level dependency tracking, so you can’t say “such and such a piece of middleware must have been executed first”;

Putting more data into the extension location requires allocating a new trait object each time, and accessing it is dynamic dispatch. Not slow, especially compared with what a dynamic language would be doing, but not as fast as if you wrote the whole stack by hand.

There are two main problems I wish to outline with all of this:

Confidence. Lack of dependency tracking means that you end up with all sorts of places where you might end up panicking, and that it’s easy to make mistakes. Debugging can also be difficult, because your extension data is approximately opaque and so you can’t easily examine or print out its contents. (Admittedly, Debug could be added as a bound in the TypeMap , MOPA-style, which would reduce its opacity to debugging.) Performance. Heap memory allocations and indirection everywhere. Imagine if you just had a simple struct containing all of this stuff, and so no heap allocations or complex lookups—​just straight field lookup. This turns something from being fairly fast to being fast.

Encoding middleware dependencies into the type system is something that we (people such as myself and Jonathan Reem) tried two years back, but the Rust type system wasn’t quite good enough back then: we collectively came to the conclusion that it just wasn’t possible, and probably wouldn’t be until the language gets HKT.

This has annoyed me from time to time. Sure, they’re not necessarily serious problems, but I do want to solve them.

I came up with a new approach last week or the week before, and decided to have one last try. Well, that particular approach didn’t work (I think—​I’ve actually forgotten most of the details of what it was now), but a part of it did and it headed me in another direction and I wound up with something that got steadily more and more complex until it worked, and then I steadily pulled bits out of it until I wound up with a drastically simplified core that also worked. Then I just had to continue cutting the macros down (Quxxy helped me with the subcontexts one, thanks!) until the whole thing of a middleware chain was just one macro with only one extraneous piece of information per middleware in the chain (and when we get eager macro expansion, there will be no extraneous information in the definitions).

The end result: Tween

The end result is a proof-of-concept middleware library for web frameworks. I’ve called it Tween, because of a certain fondness I have for Pyramid, which calls ’em that. (Aside: using traversal for URL routing instead of dispatch is really interesting. If you’re not familiar with it, you should try it purely to broaden your horizons. I don’t care whether you actually end up using it or not.)

Tween’s key features are robustness (no cause for panicking) and performance (no need for heap memory), and static tween dependency declaration.

The repository is a work in progress, a proof of concept. It has proven its concept, I believe.

What to do

Here is the code. I can’t even remember why I thought this image was a good idea. CODE IS HERE! :D

An explanation of how it works is there in that README.

This is a proof of concept. For the sake of performance it plays fast and loose with memory safety internally, and although I believe it handles everything appropriately even in case of panicking, it’d be good to have review by others. I could easily have missed something. There could also be better ways of doing some parts of it.

If you want to help me by giving feedback—​good things and bad things about the design, issues I haven’t thought of, ideas of ways of improving it, &c.—​please comment in /r/rust, file an issue in the tween repository, or drop me a line at me@chrismorgan.info.

And no, I haven’t written any benchmarks yet.