That said, if you can get your job done without this stuff, that's fine too, but the reasons it was pursued specifically involve the above.
That said, if you can get your job done without this stuff, that's fine too, but the reasons it was pursued specifically involve the above.
That being said I can't shake the feeling that going for something like Tokio in such a case is a bit like healing a paper cut by amputating the arm. Sure, technically you don't have the original problem anymore...
So, I can relate to not wanting to pull in the dependency. But otherwise it seems pretty straightforward to me. You just macro-decorate the main function, sprinkle some async/await around, maybe add a join or a mutex somewhere, and then pretty much forget all about event loops, messaging, threads and whatnot. I feel like I must be missing something important here.
Maybe it is undesirable because that often times plain mono-thread synchronous is fast enough, easier to read, easier to debug and safe to handle to a junior ? Not everybody in a team has the same level of expertise.
And Rust is not an interpreted language. IMHO, interpreted languages should just drop to a compiled one to keep it KISS. Instead of going the async road, just to discover in production, it is unstable because back-pressure was not taken into account. And, in Rust, it is probably not worth the effort and ultimately bloat most of the time.
Shared-memory concurrency is pretty much always buggy, IME, even if your team thinks they're experts.
> And Rust is not an interpreted language. IMHO, interpreted languages should just drop to a compiled one to keep it KISS. Instead of going the async road, just to discover in production, it is unstable because back-pressure was not taken into account.
WTF? Switching to a compiled language doesn't magically make your threads nonblocking. Maybe you can serve 10x more users with a compiled language, but if we're talking about slow network requests then async can make your throughput thousands of times higher.
You are reading something I did not write. I am not obsessed with the nonblocking mantra. Blocking is not inherently bad. Multi-processus is also a perfectly valid concurrency model.
Nor am I obsessing about being ultra performant to achieve the revered C10K when I don't need to or can get around it. That was my point.
Not everybody is Facebook or Netflix. For the vast majority of small and medium enterprises, it faster, simpler, safer (and possibly cheaper) to quickly develop a blocking program without thread and spawn multiple processes.
Because WTF does using a compiled language have to do with anything?
> Nor am I obsessing about being ultra performant to achieve the revered C10K when I don't need to or can get around it. That was my point.
Then what is it that you imagine using a compiled language would help with?
Writing bug-free shared memory concurrency programs with Go is trivial. Your opinion is based on outdated information.
I vaguely remember expecting a reference and getting a copy or vice versa...
I mean, I agree that Go is miles ahead of most other languages when it comes to helping prevent concurrency bugs, but it's sill tricky. Same with Rust.
Isn't that exactly what Go people push? "Don't communicate by sharing memory; share memory by communicating" and all that. If you start pushing pointers to shared memory around then I'd expect all of the problems of traditional multithreading to reappear.
Passing pointers to shared memory is the foundation of a huge number of idiomatic, performant, and productive design patterns and architectures. There exist a number of conventions and tools, like the race detector, which reduce the risks of data races to entirely reasonable levels.
that is ... not how I have experienced it. I work on building highly concurrent systems every day but async drives me insane. to me the fundamental issue is that although the code now reads linearly, it no longer executes linearly (or reasonably close to linearly), which is 1000x more confusing.
the other thing, when I'm using rust to build something high performance, part of the reason is it provides greater control. I just can't square that with macro-decorating my main function, and handing over the core control-flow to someone else's runtime.
With async/await the apparent complexity gets reduced at the cost of vastly increased actual complexity. E.g., now instead of everything being your code that you can look at and reason about, all your concurrent workloads disappear in this void that promises to do the right thing with them. If it works the way you intended, great. If it doesn't, the rabbit hole can now be really deep.
And then, it's also a trust issue. Now you have to trust other people to have done a good job.
Ok, yes, this makes sense.
And the loss of control is also an issue for me. I write code for memory-constrained environments, with blocking code and OS threads I can usually bound my memory consumption fairly easily. If I surrender the control to a scheduler runtime I feel like it becomes a lot harder, although here I'm willing to concede that it might have more to do with my lack of experience with Tokio than an objective issue.
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error>
... to this ... fn read<T: AsMut<[u8]>>(self, buf: T) ->
impl Future<Item = (Self, T, usize), Error = (Self, T, io::Error)>
is that supposed to be easier?I recently tried writing a small program that would manually poll a future to get a feel for it - utter disaster. conflicting versions of tokio, compiles but crashes because something is called outside of the tokio runtime context, etc. all the examples have #[tokio::main]-decorated main - it's like, I'm not giving you my #$(&#@(&ing main function! the programs I write have tons of stuff going on! I can't just give some library my entire control flow!
sorry for the rant! felt good to write it though.
https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h...
Like any static analysis, it’s a give and take between making sure your analysis is sound, while still allowing useful programs.
I'm not saying locks are better than async/await (although they are[1]). You're saying the borrow checker itself can't handle them in real world use?
[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
This is partly why I prefer threading over async in Rust. Look, we went through some enormous effort to make threading good and fun again. Why wouldn't you use threading?
Because the C10^nK problem where n increases periodically is still a thing?
Or rather -- async tasks are as close as you can get to green threads in rust without a runtime that would impose overhead on every program
(The borrow checker does not understand locks as a special construct, to be extra clear.)
Did you read the post I linked? It lays out the details. I am happy to clarify if you don’t get the specifics.
Sorry, my mistake!
Edit to add: futures work badly in every language, so there's no shame in the borrow checker not working with them.
Edit 2: But in that case we're back to "why would Rust want async/await over (potentially green) threads with its first-class support for locks?"
1. Rust's Journey to Async/Await - https://www.youtube.com/watch?v=lJ3NC-R3gSI
2. The Talk You've been Await-ing for - https://www.youtube.com/watch?v=NNwK5ZPAJCk
The first video goes into all the bits you're concerned about and all the things that Rust has tried before arriving where they are now
Tokio basically is green threads as a library.
Especially harmful is that common libraries like reqwest (for HTTP requests) pulls async.
async is not mandatory for reqwest. It provides a blocking API as well.
> reqwest is the default HTTP request library in Rust ecosystem
There is no "default" libraries. There are popular HTTP clients other than reqwest that also provide blocking APIs such as isahc and ureq
I am using isahc, but that also doesn't help when (say) Rusoto AWS library pulls reqwest pulls Tokio pulls async.
Basically, it isn't entirely opaque to you how it is handled.
On the other hand, there has been from day one a sort of hype around Rust's safety features, and an eagerness to promote any new library or framework written in Rust as a savior of programming. This library, as you note, will be used inappropriately (i.e. in contexts where it's not really necessary or reasonable to do) and lead to the worst kinds of bugs--those that lurk in complicated, difficult to understand code, and that are generally worse than any memory-related security vulnerability.
But for the rest of us, simple, blocking code will do just fine and save us a few headaches.
K8s is brings way more complexity and headache, so it's kind of funny that you suggest that before using async/await.
If your planning to scale up, k8s is likely something you'll want later. Additional complexity in your apps is not.
My point was that it really isn't that much additional complexity. 99.9% of the time, the main difference is that you'll have to write "await". You don't really need to know that there's a state machine hiding beneath.
https://lucumr.pocoo.org/2016/10/30/i-dont-understand-asynci...
I understand your point, but if performance isn't a concern, why use Rust at all? If the intricacies of async is that much of a burden then Rust is probably not the right tool of the job.
When you are writing high-performance server code, async is the common scenario.