I actually really love the restrictions on fairings. I'm working on a project right now that uses Node.js with Express, and there are all of these pieces of middleware that won't compose at all, and I often find myself ripping things out and putting them in new functions.
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.
> Rocket says it requires a nightly version of rust[0] - why is that necessary
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.
The author of Rocket explicitly wants to use Rocket to experiment with new features. A lot of the stuff could be done on stable (but in a more annoying way), but it's easier with experimental features. (I discussed moving it to stable with the author)
Rust Stable is great, but not all of the macro (the Macro 2.0 stuff) support is on the stable release channel yet. There are still some things you can't do on stable (or can do, but inconveniently).
(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.
How is the async web server story looking? I keep thinking I'd like to port a small tornado app to Rust, but I'd rather it was fairly easy for some to verify it was behavioural similar.
(Literally, I don't care about the performance, I just want that style of API.)
I don't know now much about tornado's API, so I can't say for sure.
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.
I was just recently looking at the state of web frameworks[1] in Rust, and it seems like the two most popular are this and Iron[2]. Can anyone comment on some of the differences between the two? Also, can anyone point out a few sites that were made using these frameworks?
Iron provides a bare minimum of concepts that you need. Rocket comes with its opinions and a lot of features nicely integrated. Iron will allow you to design anything you want easily, because all elements are as simple as they possibly can be and compose nicely, but requires that you have your own vision and enough experience to know what you need and how to build it. So Iron is great if you need thin layer of web interface added to some existing non-web project, or have enough experience with web and Rust to have own vision. It also work fine with stable. Rocket is great for all other cases - if you are new to webdevelopment and/or Rust, expect framework to tell you how to do things and don't mind using unstable. I don't have examples of sites, but none of those frameworks deals with UI, so I am not sure how would that help you.
arewewebyet hasn't been updated since March, and there's been some big developments since then.
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.
This is actually driving me a bit insane trying to do anything more than the simplest Rust samples. There are... a lot of competing libraries implementing the same concepts. Input validators, crypto, tls, http (there are I believe at least 3 http/2 implementations, and hyper doesn't even support it yet) and pretty much every concept under the sun (hell, hyper has its own implementation of a url type independent from servo_url).
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.
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.
This is actually possible with fairings! Because fairings can rewrite requests, it's possible to create a fairing that rewrites a request URI of `path/` to `path` or vice-versa as needed. Rocket will route the rewritten request normally. In psuedocode, such a fairing might look like:
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:
I'm a Rocket fan and glad to see this big slate of improvements. On the other hand, I do want to register a bit of concern regarding the plan to add "first-class database support" for 0.4. I hope that doesn't result in a close coupling to any particular database, or to using a database in general.
Database support will live outside of Rocket's core in Rocket's contrib [0] library. Everything in contrib is implemented independently of Rocket and is entirely optional to use. The implementation will be database agnostic and extensible to any database. I'm a big believer in pluggable, optional components with no forced decisions [1], and database support will follow the same philosophy.
> 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.
uhh generating a new key on launch means every time you restart the app, everyone's cookies must be reset because the old ones can't be decrypted anymore.
I don't have any experience in Rust. From what I read, Rust is a low level / system programming language. Comparing to dynamic languages like Python, I understand that Rust is much faster. However, Python is fast enough for most web applications. So why should I use Rust in web development? Even if the library ecosystem were mature enough, could I expect my productivity could become nearly high as in Python?
In terms of ecosystem maturity Rust is far behind Python, but ignoring that and performance, it still has some merits:
- 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.
I'd say it's true that Python is fast enough for most web servers. Rather than strictly considering performance, I'd guess that people seeking to do webdev in Rust are looking to leverage Rust's static analysis to improve resilience, trading off up-front productivity for long-term maintainability (which isn't to denigrate Python, which I love, but fearless refactoring really is something that compiled langs excel at). It's not the right tradeoff for everyone or every application, certainly. And maybe other people just really like Rust. :P
We also shouldn't discount it just because Python is usually fast enough (all my personal projects are in Python). Developer time is cheaper than hardware 99% of the time. For the 1% that are running millions of requests per second through their web stacks the value proposition is pretty compelling.
> fearless refactoring really is something that compiled langs excel at
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.
It takes longer to get the code to compile, but once it does the integration pains and debugging needed will be significantly less. I'd absolutely argue that productivity can be higher in Rust, especially the larger the project is, or the longer you expect to support it for.
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.
It's not that bad. Having written some Rust libraries, there are things to like that the other ecosystems don't provide. One is the rustdoc, where you document your interfaces in code, and if you write code examples in the documentation, `cargo test` will automatically check that they work.
And like I said in another comment, there is nothing as good as cargo in anywhere else.
We push millions of requests daily through Hyper 0.11, which is async and uses tokio. I've had crazy three month uptimes for these systems. Basically they just do their work fast and I don't really need to focus on fixing any problems with them.
And did I tell my test coverage is quite minimal with these projects.
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.
It's really a shame they haven't jumped on tokio yet. Then again, I tried to look at the code, and it didn't seem to be a simple dropin thing to move over to tokio.
Rocket might have an easier time doing async than, for example, Python. You get 90% of the benefit from only having the async internal - you can make all the route handlers async without needing the user to do anything else, and even if the occasional route is blocked by io you can just generate a ton of threads. It still means the "average" use case of static page returns or fast redis access will be able to handle a ton more requests.
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'm not sure it is an issue of complexity. From what I have seen, it is more a problem of ergonomics. Currently, programming something fairly simple with a few loops and if statements using Futures is painful, but I could see a Futures-based async/await version take off.
What's not clear from the front page. Is it its own Web server? Is it a framework which uses another Web server? I think that would be useful to clarify from the start.
Unlike with Python and similar single-threaded languages you can actually avoid a lot of the complexity of having async everywhere by using the CpuPool reactor for Rust futures.
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.
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