Great, I'll definitely have to try this! I've used several Rust web frameworks, including Iron[1], Nickel[2], and Rustful[3]. Each of them has their own strengths and weaknesses, so I've been launching new web sites using all of them to see what fits me best. (Until now, Rustful felt the most comfortable. But I like the others too!) Now I have one more thing to evaluate!
Before diving into this, one thing that caught me at a glance is the order of the arguments in the routing rules. It is using `/user/<int:user_id>`, but I think `/user/<user_id:int>` is more natural. A small difference, but I believe this is one thing that Bottle[4] did right. When types come later, it is easier to omit them if there's a default (e.g. `<username>` has the same meaning as `<username:str>`) and it is also consistent with the existing type annotation syntax of Rust. I suspect Flask uses the current order only because of the backward compatibility concerns. But only mitsuhiko can tell for sure.
Also, it would be great if `ViewFunc` is extended to accept the functions other than the ones returning `Result<Response, _>`, e.g. `Result<String, _>` and `Result<Vec<u8>, _>`. It is true that there exist the `From` implementations in `Response`, but it is generally more convenient to return `String` directly from the handlers rather than manually forming `Response::from("...")`. I think I can prepare a PR if you like!
Thanks again for another attempt to use Rust as a web language!
I've found that whenever I start a small web project in a new language, I always look for the "Flask" of that language. I love using a framework that gives the bare essentials and nothing else.
I don't use Rust, but your code examples look great! I understand that Rust has less opportunity for "magic" to clean things up (for example, in Python you can use a decorator to specify a route for a function), but otherwise it looks very clear. Awesome work!
I know very little Rust, but I was actually looking at how this could be done in Rust some time ago, but I didn't get anything working. All the routes (e.g. /foobar and accompanying function) needs to be collected by the "router" somehow which begs for a global route-container that the route macro (like Flasks route decorator) would need to add routes to, similar to how it is done is Flask, and, well, yeah, I couldn't find a rusty way to do it.
Bearing in mind that macros er unstable, can this be done in Rust, and if so, what would the process be?
You would hijack the `user` function to return an instance of the struct.
fn user() -> RouteUser {
// The actual function the user wrote:
fn user(_: &mut Request) -> PencilResult {
// ...
}
RouteUser {
// Generated from the annotation argument
route: "/user",
f: user
}
}
There is a difference between item decorators (`#[foobar]`) and regular procedural macros and I'm not completely sure if you could in-fact significantly change the given function. I haven't touched procedural macros in a while.
To use the above route, you would simply have a `Route` trait perhaps.
That seems like a good approach, although one thing is missing: when you use the route-decorator in Flask, the route is registered to the app object "via magic", whereas in your example, you still have to register it manually (`app.route(user())`). Now, I am partial to liking registering manually, like in your example and in Pencil itself, since that's clearer than having a lot of routes spread around in a whole lot of files that are magically registered, but just for the kicks, would it be possible to not having to manually register the route to the app object?
I'm a huge fan of flask but I actually prefer the approach of not using decorators. Without decorators its easy to see all the paths in one area and they get type-checked in rust.
You actually can use pythonish decorators in Rust, I wrote a proof of concept library for that: https://github.com/manishearth/rust-adorn (it's not really intended to be used, but if folks want to use it I'm willing to clean it up)
Probably a good idea to add "web development framework" somewhere on the page. It'll not only improve your Search engine optimization. But it also will be more explicit for the visitors.
Anyone know of any benchmarks? This seems to be built on top of hyper. I remember checking out hyper a couple months ago and being disappointed with its performance. Last I checked it was using synchronous IO, and was performing about an order of magnitude worse than equivalent Go. That could certainly change, but I'm hesitant to use Rust for an HTTP server like I would with Go until I see better performance.
Switching to asynchronous I/O isn't going to magically result in better performance on HTTP workloads. I don't think most of what any performance difference you're seeing is due to that: I suspect instead that it's relatively "boring" optimization work that has yet to be done in Hyper.
The primary difference between async and synchronous I/O is (a) better memory usage due to not having a stack per connection; (b) you can avoid the overhead of context switches to wake up an I/O thread; (c) thread spawning performance is faster due to the kernel not having to be involved. Golang's advantages in (a) and (b) are much less than what is typically thought of as "async I/O", because it still semantically uses a thread-per-connection and so is performing the same operations that a synchronous I/O implementation performs, just with a different implementation strategy.
Go's networking library actually does call epoll/kqueue/etc on the backend, so it is using nonblocking io. Using nonblocking IO will provide better performance because it will increase the number of concurrent requests it can serve. It's not a silver bullet, and can be very difficult to implement at the application layer to avoid blocking, but it will give markedly better performance.
But one of the reasons you're able to process more with async I/O is bypassing the costs of the thread-per-connection model. So you're not forced to store all the thread's stacks and you don't have expensive context switches.
As the parent illustrated, even with Go using nonblocking I/O, it's perceived benefits in that area isn't that great because Go still semantically has a thread-per-connection. So the performance characteristics of Go isn't simply async vs sync I/O.
>But one of the reasons you're able to process more with async I/O is bypassing the costs of the thread-per-connection model.
One reason, not the only, or the most important. The primary advantage of non blocking IO is that the CPU isn't sitting idle while waiting for an IO operation to complete. We aren't wasting an entire core to write the response.
That's not how blocking I/O works! Sitting in a busy loop on the CPU burning power polling the I/O device was, like, how the Apple II may have worked, but it hasn't worked like that on any major OS since at least 1990. Operating systems perform context switches on I/O.
And if you accept that (which I'm not sure I do, but anyhow) then you accept that there isn't much difference between userspace M:N and blocking 1:1 threading.
Right, so Hyper would be faster if it used true asynchronous I/O, right? That was my whole point. I just brought up Go so I had something to compare it to.
Hyper may very well be much faster, but it isn't. Which is the whole issue with the Rust vs Go debates: one is possibly much better, but the other one gets all the code written for it in 1/6th of the time (Rust came out the same time Go did, to great fanfare).
Are we willing to wait 6x longer for rust to catch up? Some people are, most people aren't.
I don't know whether you're talking about a 6x development speed differential or a 6x performance difference between Rust and Go HTTP stacks or what, but I am pretty sure that by making up numbers like that you're language warring for no reason.
yes, in this case i am language warring for no reason. i'm an unapologetic go devotee but i like rust just the same. i wish people would just get on with things and write software, rather than comment on minutiae on message boards.
That's nice and all, but unfortunately the real world tests I've seen (and done) show something like Netty completely trumping anything available for Rust.
1. Are you testing against mio? If so, have you filed bugs against it? At a high level, there is no fundamental design difference between Netty and mio.
2. Netty is worlds away from Golang, precisely for the reasons stated above. Go is a userspace, M:N implementation of per-thread, blocking I/O, while Netty is a truly asynchronous I/O framework. Of course truly async I/O can beat synchronous I/O, but the entire point is that Golang is not strictly async I/O.
This is a good slogan, but there's some subtleties when it comes to HTTP status codes. That is, there are the defined ones, but any number is legit, so you end up with an enum with a member that's basically "anything we don't know about", so it's not as clear-cut as it is in other situations.
Good point, this is where Rust's flexible enums are an advantage relative to C-style. If you'll forgive the Haskell syntax, you can have something like:
data StatusCoode
= Ok200
| Missing404
| Error500
| OtherStatusCode Int
To rephrase the slogan a bit - make an illegal state a bit more difficult to represent or at least the risk of an illegal state clear to the programmer.
And of course a problem with this is any status code now has two representations: the canonical enum variant, and the catch-all variant with the same numeric value.
Well, 100- and 300- class respones aren't errors, but yeah, 400- and 500- might make more sense as Err.
You could also look at it from another point-of-view. Ok() means that I am able to fully handle this request, even if it's an HTTP error code, and Err being "nope, don't know. Sorry bud".
Although Iron is powerful, I find there is a lot of complexity around it. The middleware concept mostly contributes to this in addition to the abstractions over Hyper constructs.
Flask is my favorite tool for writing web apps in python. Django is nice, but I like the idea of building a framework up around the application, instead of shoehorning an application into a framework, and Flask lets you only bring in the parts you need.
I would love to see how Rust will influence the web development sphere and I'd like to see if it would cut down on some common bugs that are made in production web development.
That is also, I think, one of flask's biggest weaknesses. It does make everything simpler though. I'm just not very fond of magic globals. It also doesn't fit very well into REST and HTTP. On the other hand, I love flask for just about everything else.
Very nice and readable. What are the plans for this? Anyone share to comment if there is a development roadmap or if its just an exercise in programming.
It looks like it's a bit of a pet project at the moment, but these sorts of things have a habit of taking off in a big way sometimes. Probably a big "it depends".
Great, I'll definitely have to try this! I've used several Rust web frameworks, including Iron[1], Nickel[2], and Rustful[3]. Each of them has their own strengths and weaknesses, so I've been launching new web sites using all of them to see what fits me best. (Until now, Rustful felt the most comfortable. But I like the others too!) Now I have one more thing to evaluate!
Before diving into this, one thing that caught me at a glance is the order of the arguments in the routing rules. It is using `/user/<int:user_id>`, but I think `/user/<user_id:int>` is more natural. A small difference, but I believe this is one thing that Bottle[4] did right. When types come later, it is easier to omit them if there's a default (e.g. `<username>` has the same meaning as `<username:str>`) and it is also consistent with the existing type annotation syntax of Rust. I suspect Flask uses the current order only because of the backward compatibility concerns. But only mitsuhiko can tell for sure.
Also, it would be great if `ViewFunc` is extended to accept the functions other than the ones returning `Result<Response, _>`, e.g. `Result<String, _>` and `Result<Vec<u8>, _>`. It is true that there exist the `From` implementations in `Response`, but it is generally more convenient to return `String` directly from the handlers rather than manually forming `Response::from("...")`. I think I can prepare a PR if you like!
Thanks again for another attempt to use Rust as a web language!
[1] http://ironframework.io/
[2] http://nickel.rs/
[3] https://github.com/Ogeon/rustful
[4] http://bottlepy.org/