Really nice introductory article, though some of the more… axiomatic assertions, are iffy.
> If it’s possible to build more reliable systems with programming languages with stricter constraints, what about languages with the strongest constraints? I’ve skewed all the way to the far end of the spectrum and have been building a web service in Rust
Bit overselling it, that introduction would work better for something like ATS or possibly Idris. Rust has its strictness, but it's not exactly "you must prove this recursion terminates"-strict
> Synchronous operations aren’t as fast as a purely asynchronous approach
This is misleading. Synchronous sequences are generally faster than asynchronous ones because yielding and getting scheduled back has a non-zero overhead (this is regularly re-discovered, most recently by the developers of "better-sqlite": https://news.ycombinator.com/item?id=16616374).
Async operations are a way to increase concurrency when your synchronous sequence would just be waiting with its thumb up its ass (e.g. on a network buffer filling or on a file read completing), so you can provide more service over the same wallclock time by (hopefully) always using CPU time.
> Synchronous sequences are generally faster than asynchronous ones because yielding and getting scheduled back has a non-zero overhead
Indeed. I remember being shocked when I first read "Thousands of Threads and Blocking I/O" [0] by Paul Tyma.
After reading the C10k problem [1], I just believed that a big pile of threads would never work and never updated my beliefs. While such an approach is still quite memory limited compared to asynchronous approaches, it scales quite far and can have a very high work rate.
> This is misleading. Synchronous sequences are generally faster than asynchronous ones because yielding and getting scheduled back has a non-zero overhead
To get a bit pedantic, synchronization introduces overhead. If you've got an 8-core processor running things async/parallel may complete faster since you aren't pinned to one core.
That said, it's much harder to get right since you have to deal not only with thread sync overhead but also cache/pipeline sharing and a bunch of other gnarly things.
No. Implicitly yielding and getting scheduled back is exactly what synchronous sequence is. And it cannot ever be faster than asynchronous, because asynchronous doesn't yield. Any "rediscoveries" are just mistakes due to lack of fundamental understanding of these things.
Why would you want to do that in Rust instead of Go / Java / C# since the performance is the same. When I look at code example in the article the syntax is just awful seriously, who want to write web services with that kind of syntax:
Rust is a good language but I don't see it any time soon in the web space with that syntax and the lack of mature libraries, plus the time it takes to get anything done. Where I see it is more for super optimized things like serialization and the like as libraries but not as a main program.
Nonsense to you is wonderful to me. I don't think "I am unfamiliar with this syntax" is really a useful observation - Erlang has an extremely unfamiliar syntax, but I don't think that makes it less suitable for building a service.
To answer your question more directly (though I feel the article covers these points), as someone building services in rust, there are a few reason I'd choose Rust over the languages you listed:
a) Largely deterministic performance
Having written services professionally in Java I have seen performance scale linearly with connections, until one day it suddenly spikes massively - why? It was that we crossed some threshold where the GC was suddenly having to do a ton more work, which caused a serious feedback loop - server has to pause, clients don't handle it well, clients hammer server more, server has to pause, etc.
You can have similar issues in Rust but they should be easier to spot (they should be less blow-up-y).
b) Correctness/ Expressiveness (is this a word?)
I find it far, far easier to write correct, expressive code in Rust. For example, I think anyone who has moved from Java 6 to 7 or 8 will admit that Optional is awesome - because it is. In Rust the pattern exposed by Optional is first class, and extends far beyond nullability. This is just one example though.
I find it extremely easy, by comparison to other languages, to write Rust code in a 'type driven' way, and in my experience this is a huge part of (me) writing code with fewer defects.
As the linked article puts it:
> Constraints like a compiler and a discerning type system are tools that help us to find and think about those edges.
I'm personally a big advocate of Rust. That said, like yourself, I'm also a little skeptical that it's the right tool for most web apps. I think a case can be made for small Rust web apps embedded in consumer electronics that may be very resource constrained, though. (Granted, these probably aren't the sorts of apps that would be using PostgreSQL as described in the article.) I've seen a lot of great web applications written in Go that can also be quite compact (compared to Java, for instance), and I think Go might be a great candidate for the next level up in resource availability.
Like many languages, the syntax becomes a lot more clear after you've used the language a while. Maybe it's me who's been warped, but I've been programming in Rust for long enough that the example you pasted looks perfectly normal. ;)
> Rust is a good language but I don't see it any time soon in the web space with that syntax and the lack of mature libraries, plus the time it takes to get anything done.
Lack of mature libraries is always a problem, syntax is subjective and "time it takes go get anything done" .. which time do you measure? Time-to-first-running-example or Time-to-debugged-productions-ready-code? I have my doubts that Rust takes significantly longer for the second one.
The syntax is not that bad but there are some poor variable names that make the code more difficult to grasp.
The motivation to learn a new language and the excitement during the process are huge factors that will push many people to write a web service in Rust.
I've been using 'actix-web' in anger for the last month or so and I just want to echo that it is an amazingly fast and fully-featured project.
I smiled when reading this post because I absolutely recognize the euphoria of carrying out a world-changing refactor and having it work first time. If this is what the future of backend development feels like, sign me up!
I’ve been using rust moderately for the last 6 months and at least my experince was that once you get used to playing along with the compiler and understand the fundamental features of the language, it almost feels too good to be true.
Perfect mix of high performance, safety and modern features. Slightly challenging at first, but it really is worth the initial pain.
The author briefly mentions Haskell but I wonder whether they've build web services with it, or with another (loosely) ML-family language (e.g. OCaml, F#, or my personal favourite Scala). An ML-style type system is a huge advantage over Ruby/JavaScript/..., but for a web service I struggle to see a real case for going to all the effort of memory ownership tracking rather than using a GCed language with the same kind of strong type system; it would be good to see a comparison from someone who's done both.
I think that despite the constraints imposed by the lack of GC, Rust is seen as more approachable than more functional langauges to those coming from a C/Java/JavaScript etc background. And the only similar language with an ML type system is Swift which isn't quite there with libraries for backend stuff.
Also, have you seen the sample code for Rocket based webapps? I haven't seen much nicer in any language. The async ecosystem isn't nearly as nice yet, but it should get much nicer once async-await lands.
> It’d be fair to say that I could’ve written an equivalent service in Ruby in a tenth of the time it took me to write this one in Rust.
Ha, I had the same feeling when I needed to create a simple REST service that'd just process a request using command line tools. Seems easy but Rust's tooling is full of rough edges. Code completion sometimes works sometimes doesn't, cargo cannot install dependencies only (without building source) so it's not good for Dockerfile layers, etc. etc. Borrow checker is not bad at all for people that worked with languages with explicit memory management (where you do ownership tracking in your head anyway).
Long story short I spent 2 days working on the service and had 80% done but figured out the rest would take twice as much if I want this to be production quality. I scraped the project and rewritten it in node, using one or two dependencies in 2 hours.
I'll be trying Rust again surely and I'm glad that articles like this one exist!
Just an FYI (if you want something to watch and haven't heard about it already) https://github.com/tower-rs/tower-grpc is in the works by the same folks building tokio and hyper etc.
But yep, definitely not production ready as the README clearly states :)
Specifically the custom bindings using Diesel. I was unaware that generic sql could be bound so easily. That’s closer to how I end up when needing to create higher performance queries. I need to take another look at Diesel now.
I love rust, but to be honest, until the nightmare that is tokio/futures is fixed with native async/await and better compiler error messages, a strongly typed language like C# with those features natively present is my choice for web services. It addresses all the author’s issues with Ruby and JS, and is still orders of magnitude faster than those options (though admittedly not as fast as a C or rust option).
Very recently I've taken a tour around the Rust web server ecosystem, and I think it's way too hard to find one to pick.
The author mentions actix-web[0], and mentions how fast it is, but it's actually right below hyper[1], which I find to be simpler, because it doesn't bring in the whole actor frame work thing. Hyper builds on Tokio[2] which introduces the usual event loop paradigm that we're used to in other languages (which AFAIK is the fastest way to write web servers to date). There's also great talk[3] that introduces the choices that tokio makes that are somewhat unique.
Here's what I want out of a web framework:
- express like-interface (func(req,resp,next) pattern provies just the right amount of freedom/structure IMO)
- good routing (plus: decorators/a fairly ergonomic way to specify routes)
- reasonable higher-level interfaces to implement (I want to implement an interface, and be able to receive things like logging, tracing, grpc/thrift/whatever transports for free, good interfaces are what make writing reusable stuff like that possible)
Here's what I found about the various frameworks that exist out there:
- Rocket.rs[4]: Seems to do too much (I tend towards building APIs), rocket is closer to django/rails than it is to flask/sinatra.
- Gotham.rs[5]: Seems perfect, but falls flat on the feature front, half the stuff on the landing page are features of rust, not the library. Doesn't seem to have enough batteries included, for example there's currently work being done on streaming request bodies (https://github.com/gotham-rs/gotham/issues/189), that's not a issue I want to run into.
- Iron.rs[6]: The oldest of the bunch, but also very very fast (which I discovered actually browsing rocket's issues[7]), not based on hyper, and also not actively maintained.
However, when you look at that comparison, it's really suspicious how much of actix-web has filled out, which is often indicative of something written from one point of view (like when people have comparison pages, and one option seems to just have "everything"). And again, like I said actix seems to be doing too much, I don't really want to utilize the actor model, I just want a relatively fast, ergonomic simple library with the most basic batteries included.
BTW, everyone keeps raving about type safety and I wonder why people don't give Haskell more of a go. If Rust gives you the type safety equivalent to a shield, haskell is a phalanx. I've never felt more safe and protected by my types than when I write Haskell -- it's relatively fast, memory safe, has fantastic concurrency primitives, and though it can be tough to optimize (which comes to down to optimizing for lower amounts of GCs like most other memory managed languages), it's pretty fast out of the gate. I use servant (https://haskell-servant.readthedocs.io/) and love it.
[+] [-] masklinn|8 years ago|reply
> If it’s possible to build more reliable systems with programming languages with stricter constraints, what about languages with the strongest constraints? I’ve skewed all the way to the far end of the spectrum and have been building a web service in Rust
Bit overselling it, that introduction would work better for something like ATS or possibly Idris. Rust has its strictness, but it's not exactly "you must prove this recursion terminates"-strict
> Synchronous operations aren’t as fast as a purely asynchronous approach
This is misleading. Synchronous sequences are generally faster than asynchronous ones because yielding and getting scheduled back has a non-zero overhead (this is regularly re-discovered, most recently by the developers of "better-sqlite": https://news.ycombinator.com/item?id=16616374).
Async operations are a way to increase concurrency when your synchronous sequence would just be waiting with its thumb up its ass (e.g. on a network buffer filling or on a file read completing), so you can provide more service over the same wallclock time by (hopefully) always using CPU time.
[+] [-] nordsieck|8 years ago|reply
Indeed. I remember being shocked when I first read "Thousands of Threads and Blocking I/O" [0] by Paul Tyma.
After reading the C10k problem [1], I just believed that a big pile of threads would never work and never updated my beliefs. While such an approach is still quite memory limited compared to asynchronous approaches, it scales quite far and can have a very high work rate.
[0] https://www.slideshare.net/e456/tyma-paulmultithreaded1
[1] http://www.kegel.com/c10k.html
[+] [-] nicoburns|8 years ago|reply
[+] [-] vvanders|8 years ago|reply
To get a bit pedantic, synchronization introduces overhead. If you've got an 8-core processor running things async/parallel may complete faster since you aren't pinned to one core.
That said, it's much harder to get right since you have to deal not only with thread sync overhead but also cache/pipeline sharing and a bunch of other gnarly things.
[+] [-] zzzcpan|8 years ago|reply
[+] [-] Thaxll|8 years ago|reply
time_helpers::log_timed(&log.new(o!("step" => "upsert_episodes")), |_log| { }
It's just non-sense:
impl<S: server::State> actix_web::middleware::Middleware<S> for Middleware {
Rust is a good language but I don't see it any time soon in the web space with that syntax and the lack of mature libraries, plus the time it takes to get anything done. Where I see it is more for super optimized things like serialization and the like as libraries but not as a main program.[+] [-] staticassertion|8 years ago|reply
To answer your question more directly (though I feel the article covers these points), as someone building services in rust, there are a few reason I'd choose Rust over the languages you listed:
a) Largely deterministic performance
Having written services professionally in Java I have seen performance scale linearly with connections, until one day it suddenly spikes massively - why? It was that we crossed some threshold where the GC was suddenly having to do a ton more work, which caused a serious feedback loop - server has to pause, clients don't handle it well, clients hammer server more, server has to pause, etc.
You can have similar issues in Rust but they should be easier to spot (they should be less blow-up-y).
b) Correctness/ Expressiveness (is this a word?)
I find it far, far easier to write correct, expressive code in Rust. For example, I think anyone who has moved from Java 6 to 7 or 8 will admit that Optional is awesome - because it is. In Rust the pattern exposed by Optional is first class, and extends far beyond nullability. This is just one example though.
I find it extremely easy, by comparison to other languages, to write Rust code in a 'type driven' way, and in my experience this is a huge part of (me) writing code with fewer defects.
As the linked article puts it:
> Constraints like a compiler and a discerning type system are tools that help us to find and think about those edges.
[+] [-] simmons|8 years ago|reply
Like many languages, the syntax becomes a lot more clear after you've used the language a while. Maybe it's me who's been warped, but I've been programming in Rust for long enough that the example you pasted looks perfectly normal. ;)
[+] [-] qaq|8 years ago|reply
#[get("/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
[+] [-] pcwalton|8 years ago|reply
Here's an example of some Rust code that's even faster and is short and sweet: https://github.com/tokio-rs/tokio-minihttp/blob/master/examp...
(I don't think the TechEmpower benchmarks really mean much, incidentally. They're so simple that they're way too easy to game.)
[+] [-] sgift|8 years ago|reply
Lack of mature libraries is always a problem, syntax is subjective and "time it takes go get anything done" .. which time do you measure? Time-to-first-running-example or Time-to-debugged-productions-ready-code? I have my doubts that Rust takes significantly longer for the second one.
[+] [-] yani|8 years ago|reply
The motivation to learn a new language and the excitement during the process are huge factors that will push many people to write a web service in Rust.
[+] [-] mseepgood|8 years ago|reply
[+] [-] oblio|8 years ago|reply
Why did Rust go with the IMHO ugly closure syntax of
?I think that
is much nicer to look at... :(Also, not presented here, but ‘ is also kind of ugly, was there no short keyword that could have been used? “life or “lt” for “lifetime”? “span”?
[+] [-] adrianN|8 years ago|reply
Also, where do you get that the performance is the same? I didn't see any benchmarks.
[+] [-] _wc0m|8 years ago|reply
I smiled when reading this post because I absolutely recognize the euphoria of carrying out a world-changing refactor and having it work first time. If this is what the future of backend development feels like, sign me up!
[+] [-] dorfsmay|8 years ago|reply
[+] [-] illuminati1911|8 years ago|reply
I’ve been using rust moderately for the last 6 months and at least my experince was that once you get used to playing along with the compiler and understand the fundamental features of the language, it almost feels too good to be true.
Perfect mix of high performance, safety and modern features. Slightly challenging at first, but it really is worth the initial pain.
[+] [-] oromier|8 years ago|reply
[deleted]
[+] [-] lmm|8 years ago|reply
[+] [-] nicoburns|8 years ago|reply
Also, have you seen the sample code for Rocket based webapps? I haven't seen much nicer in any language. The async ecosystem isn't nearly as nice yet, but it should get much nicer once async-await lands.
[+] [-] Shoothe|8 years ago|reply
Ha, I had the same feeling when I needed to create a simple REST service that'd just process a request using command line tools. Seems easy but Rust's tooling is full of rough edges. Code completion sometimes works sometimes doesn't, cargo cannot install dependencies only (without building source) so it's not good for Dockerfile layers, etc. etc. Borrow checker is not bad at all for people that worked with languages with explicit memory management (where you do ownership tracking in your head anyway).
Long story short I spent 2 days working on the service and had 80% done but figured out the rest would take twice as much if I want this to be production quality. I scraped the project and rewritten it in node, using one or two dependencies in 2 hours.
I'll be trying Rust again surely and I'm glad that articles like this one exist!
[+] [-] vardump|8 years ago|reply
That said, one of the only remaining things keeping me from using it is production ready gRPC library.
[+] [-] kehtnok|8 years ago|reply
But yep, definitely not production ready as the README clearly states :)
[+] [-] bluejekyll|8 years ago|reply
Specifically the custom bindings using Diesel. I was unaware that generic sql could be bound so easily. That’s closer to how I end up when needing to create higher performance queries. I need to take another look at Diesel now.
Very nice article.
[+] [-] staticassertion|8 years ago|reply
This is a very helpful post for me.
[+] [-] ComputerGuru|8 years ago|reply
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] rabidferret|8 years ago|reply
[+] [-] hardwaresofton|8 years ago|reply
Very recently I've taken a tour around the Rust web server ecosystem, and I think it's way too hard to find one to pick.
The author mentions actix-web[0], and mentions how fast it is, but it's actually right below hyper[1], which I find to be simpler, because it doesn't bring in the whole actor frame work thing. Hyper builds on Tokio[2] which introduces the usual event loop paradigm that we're used to in other languages (which AFAIK is the fastest way to write web servers to date). There's also great talk[3] that introduces the choices that tokio makes that are somewhat unique.
Here's what I want out of a web framework:
- express like-interface (func(req,resp,next) pattern provies just the right amount of freedom/structure IMO)
- good routing (plus: decorators/a fairly ergonomic way to specify routes)
- reasonable higher-level interfaces to implement (I want to implement an interface, and be able to receive things like logging, tracing, grpc/thrift/whatever transports for free, good interfaces are what make writing reusable stuff like that possible)
Here's what I found about the various frameworks that exist out there:
- Rocket.rs[4]: Seems to do too much (I tend towards building APIs), rocket is closer to django/rails than it is to flask/sinatra.
- Gotham.rs[5]: Seems perfect, but falls flat on the feature front, half the stuff on the landing page are features of rust, not the library. Doesn't seem to have enough batteries included, for example there's currently work being done on streaming request bodies (https://github.com/gotham-rs/gotham/issues/189), that's not a issue I want to run into.
- Iron.rs[6]: The oldest of the bunch, but also very very fast (which I discovered actually browsing rocket's issues[7]), not based on hyper, and also not actively maintained.
I had no idea which of these to use, or how to find ones I've missed, then I stumbled upon this amazing resource: https://github.com/flosse/rust-web-framework-comparison.
However, when you look at that comparison, it's really suspicious how much of actix-web has filled out, which is often indicative of something written from one point of view (like when people have comparison pages, and one option seems to just have "everything"). And again, like I said actix seems to be doing too much, I don't really want to utilize the actor model, I just want a relatively fast, ergonomic simple library with the most basic batteries included.
BTW, everyone keeps raving about type safety and I wonder why people don't give Haskell more of a go. If Rust gives you the type safety equivalent to a shield, haskell is a phalanx. I've never felt more safe and protected by my types than when I write Haskell -- it's relatively fast, memory safe, has fantastic concurrency primitives, and though it can be tough to optimize (which comes to down to optimizing for lower amounts of GCs like most other memory managed languages), it's pretty fast out of the gate. I use servant (https://haskell-servant.readthedocs.io/) and love it.
[0]: https://github.com/actix/actix-web
[1]: https://hyper.rs/
[2]: https://tokio.rs/
[3]: https://www.youtube.com/watch?v=4QZ0-vIIFug
[4]: https://rocket.rs/
[5]: https://gotham.rs/
[6]: http://ironframework.io/
[7]: https://github.com/SergioBenitez/Rocket/issues/552
[+] [-] kbenson|8 years ago|reply
[+] [-] huntie|8 years ago|reply