First Impressions about New Rust Async

Just recently async and await made it to stable version of Rust. futures library 0.3 is also out and rest of other important libraries is following – for me tokio and hyper are particularly interesting. tokio is now in alpha stage supporting new futures, so I decided it’s about a time to give it a try. Some time ago I’ve rewritten ptunnel in Rust using 0.1 version of tokio and futures. So here are my first experiences with new libraries and upgrade.

ptunnel is simple program which tunnels local connections through HTTP proxy (through HTTP CONNECT method). As it just forwards bytes back and forth, asynchronous programming is ideal for maximal throughput and minimal resources used. So I was wondering if I can get some more improvements with new tokio.

Before coding I looked a bit around Internet and found this article useful (many finding there where confirmed by my efforts). Also there is a compatibility layer to use old and new futures together, but fortunately I did not need backward compatibility.

I started with updating dependencies and removing some outdated. Great news is that new tokio has already asynchronous DNS resolution built in so no external library was needed. Changes in libraries version immediately lead to something like ~60 errors in code (and those were the easier ones, most tricky problems – related to lifetime and ownership compilers saves to the last minute, you fix seemingly last error and whoa-la – new and tougher errors pop up).

So I started to go through code. Biggest change is difference in Future type where Future now returns one generic Output associated type , instead of Result with two associated types. Indeed you can use result too here, but type signature is different. Also signature of Futute’s poll method changed – self reference is now pinned and there is second parameter – Context. It also means that all Result oriented combinators like and_then, map_or … disappeared from basic Future (but are available on TryFutureExt trait). Future trait itself split into two basic one Future and FutureExt with all combinators.

Other big change is new async functions and blocks and await keyword. They are absolutely cool, with their help you can simplify code significantly. Also using them limits need for future combinators and explicit use of Future type, so it also helped greatly with first problem during changes.

Also runtime API changed a lot – as I’ve been using explicit runtime initialization , I had to look for new API, but generally it’s nice and clean.

One of biggest issues I have was that some things I used are recently hidden behind features so it took me some time to realize that and use correct dependency version (latest git) with full features. Also current thread executor seems to have some issues now. But overall I was able to migrate to new code after half day or so.

New code is much more cleaner and I was able to get rid of some unnecessary wrapper code in my previous version (where I use Future to process CONNECT header, now it can be done more easily with async/await). async/await programming style is much closer to what I’m used to from python/javascript, which one huge advantage – static checks – if you forget to await, you’ll get compile time error.

And how new code performs? In past article I was measuring throughput with ab tool (in setup ab <-> ptunnel <-> squid <-> nginx on local host) and got about 0.31ms per request (on larger number of requests on keep-alive connections). Now in similar setup I’ve got 0.067ms, so about 4 times better, which is quite nice.

So overall quite positive experience, now I’ll wait a bit until new tokio and hyper will get stable and will look into my other project audioserve, which is much bigger, so much more work expected. I wish stable versions were available two years back when I started it, but as it’s being said “repetitio est mater studiorum”.

Leave a Reply

Your email address will not be published. Required fields are marked *