Pencil: A Microframework Inspired by Flask for Rust
fengsp.github.io
fengsp.github.io
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 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!
#[route("/foobar")]
fn user(_: &mut Request) -> PencilResult {
// ...
}
However, procedural macros are unstable, but Rust is able to do amazing things with macros.Bearing in mind that macros er unstable, can this be done in Rust, and if so, what would the process be?
struct RouteUser {
route: &'static str,
f: fn(_: &mut Request) -> PencilResult
}
Then given #[route("/user")]
fn user(_: &mut Request) -> PencilResult {}
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.
trait Route {}
And implement it for each generated struct: impl Route for RouteUser {}
Then you could use the route as app.route(user());
Which could be defined as fn route<R>(r: R) where R: Route {
// ...
}I always look for the "Sinatra" of that language.
Rust is that thing exactly.
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.
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.
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.
which are very cheap :)
I know, of course. If you reread my comment, you'll notice that it wasn't focusing on the particular syscall that the system uses.
> Using nonblocking IO will provide better performance because it will increase the number of concurrent requests it can serve.
Because of (a), (b), and/or (c), which I addressed in my comment above.
Are we willing to wait 6x longer for rust to catch up? Some people are, most people aren't.
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.
As Jane Street Says, make illegal state unrepresentable.
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.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".
Since it's integral to error handling in Rust you probably can't get away from it.
That said, I've been very happy with Iron thus far.
app.get("/", "hello", hello);
app.get("/user/<int:user_id>", "user", user);
....
This reminds me of Pyramid more than Flask though, just my personal observation. The code looks pretty readable even from a non-Rust programmer.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.
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".
"The Blueprint²: The Gift & the Curse"
Hum, yes, that is what the author says :)