By having clear delineations between fairings, request guards, and data guards, I think you can really avoid making a lot of design mistakes. I'm going to try out Rust some more and definitely play around with this framework! The only thing that bothers me is that Rocket says it requires a nightly version of rust[0] - why is that necessary? I thought Rust was pretty stable by now.
[0] https://rocket.rs/guide/getting-started/#minimum-nightly
I often joke that Rocket deliberately uses every single unstable feature solely to stymie the Rust developers' attempt to prioritize which features to stabilize. :P I'm kidding of course (hi, sbenitez!), but it's true that I've never seen any Rust project so taken with nightly features as Rocket is.
(Rocket also depends on a few other nightly only features. Things will likely change in the future once the Macro stuff stabilizes though)
There is also the matter of compiler plugins... which may never land in stable.
So far all the Rust web frameworks I've seen have pretty disappointing performances.
I was expecting C++/Java/Go level of performance. Instead, Tokio & Iron turn out to be slower than many frameworks in Ruby, Python, PHP, JS:
https://www.techempower.com/benchmarks/#section=data-r14&hw=...
https://www.techempower.com/benchmarks/#section=data-r14&hw=...
https://www.techempower.com/benchmarks/#section=data-r14&hw=...
* Tokio-minihttp is #4 overall on the plaintext benchmark there, which is sort of the "what is the max performance" benchmark
I don't know of anyone who is really actively looking at Techempower and optimizing based on it, which is how you win benchmarks.
(Literally, I don't care about the performance, I just want that style of API.)
Regarding fairings, it seems a missing "middleware" case might be the sorts of things that cause redirects on entry (e.g. redirect routes with/without trailing slashes to the latter as a super trivial example). Is that something you'd expect to support in some way? I think that's something that doesn't feel like it maps either to guards or fairings well at the moment.
I did see where you mentioned your dislike of rails/sinatra/... style blunt force middleware, fwiw.
on_request => |request, _| {
if request.uri().path().ends_with('/') {
let new_path = request.uri().path()[..-1];
request.set_uri(URI::new(new_path));
}
}
You can also use a fairing If you want to return a 302 (or similar) so that the browser does the redirect instead. In this case, you'd implement a response fairing that rewrites failed responses to return a redirect to the appropriate URI. Again, in pseudocode, this would look like: on_response => |request, response| {
if response.status() == Status::NotFound && request.uri().path().ends_with('/') {
response.set_status(Status::Found);
response.set_header(Location(request.uri().path()[..-1]));
response.take_body();
}
}
Take a look at the fairings guide [0] and fairings documentation [1] for more ideas![0]: https://rocket.rs/guide/fairings/
[1]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html
People inevitably have to deal with preexisting systems they can't modify for their needs.
> To encrypt private cookies, Rocket uses the 256-bit key specified in the secret_key configuration parameter. If one is not specified, Rocket automatically generates a fresh key at launch.
Seems like a pretty clever idea. Do other servers/middlewares offer a similar feature? Seems like it would complicate deployment/scaling a bit if the secret has to be sent to all the nodes. Especially if they could silently ignore it if you accidentally don't configure the key for some nodes.
Even going all the way, I don't see much more of an issue than just exposing a handle to routes to do async IO with if they want to and making the internal async core available to fairings and other code from main().
Internally it would probably be a big mess right now, though.
I imagine Rocket, when it gets its async support, will have the ability to use callbacks to create fully async routes, but the halfway compromise until then will be to have async-dispatched routes that are themselves blocking to a very large threadpool. As long as you have enough generated threads to rarely get all of them stuck in blocking IO you get all the benefits of concurrency without having to callback every IO operation in a route handler.
Those probably are the two most popular, but there's a Cambrian Explosion going on right now, with new frameworks like Susanoo and Cargonauts appearing on the regular. Heck, I even have my own little half-baked one.
There are several Json types across all the various crates, multiple Url types, multiple Http::Request or Hmac or SHA cipher types. They all use the same names but are always distinct constructs that cannot interop.
It might be one of the most significant ergonomic issues with Rust going forward. Having a dozen Url types in Python is fine since the typing usually just coerces them all to Strings for interop anyway. Most Url types are (thankfully) serializiable and have a to_string, but in a statically typed language the performance implications of having the compiler generate strings from objects to be consumed by another object's constructor are dreadful.
- static analysis helps you avoid numerous issues that plague Python. Runtime errors are far less likely and you are forced to greater discipline when coding
- it is modern language. After using algebraic data types, pattern matching and traits it is hard to go back to old style OOP.
- You can extend existing types with your own traits, and so can library authors, so you have plenty of extremely useful extensions. Just add use clause referencing some library, and everything gets more useful methods, without any magic or monkeypatching, so it causes no issues as it could in some dynamic languages.
I'd say it is massive gain in terms of quality and maintainability, but at the cost of upfront investment. Rust isn't a language I'd expect people to write prototypes in, but with few crates it can be as expressive as Python in many cases.
- The Cargo dependency and build tool is the best in any ecosystem. Easy to configure, works like a charm and gets improvements done regularly.
I don't miss setup.py/virtualenv/pip/pex at all.
Would you say that's a feature of compilation? I'd have said it's Rust's strong and static type system that makes refactoring 'fearless' and generally aids productivity and maintainability.
Python ≥3.6 with liberal type hinting is a more interesting comparison in my view.
A lot of what people miss when approaching Rust is that writing libraries is really hard and time consuming, but writing applications using libraries is often as easy as Python. Libraries need to provide abstractions, and thus use really complex features like code generation macros, trait extension, generics, type conversion, and the annoying lifetimes that most people beat their heads on when first learning Rust. Rust for an application developer feels almost entirely OOP, like Java, and much nicer than C++ because simple data structures don't hit undefined behavior every other line with the other half being full of memory leaks.
And like I said in another comment, there is nothing as good as cargo in anywhere else.
And did I tell my test coverage is quite minimal with these projects.
Right now, the async APIs are largely based on Futures. There's an experimental branch of the compiler that implements coroutines and therefore async/await on top of them though, so that syntax may or may not be coming in the future.
It's just a convenience feature for development.
Be aware that hyper is still actively evolving towards 1.0, and is likely to experience breaking changes before stabilising. You can also see the 1.0 issue milestone.
https://github.com/SergioBenitez/Rocket/blob/master/codegen/...
I find the exact opposite to be true in most cases.
Hyper 0.11 with Tokio is the thing to choose for http/1. It's fast and composes nicely. Http/2 support is coming soon and looking pretty nice already.
Personally I'm more tempted by something like Susanoo: https://github.com/ubnt-intrepid/susanoo/