3 ms·
Let me try to rephrase this in a way that doesn't pin the blame on "async enthusiasts" as people, and see if you agree: Many years ago, well before Rust 1.0, R
by geofft 6y ago
Let me try to rephrase this in a way that doesn't pin the blame on "async enthusiasts" as people, and see if you agree:
Many years ago, well before Rust 1.0, Rust used its own M:N threading system, used segmented stacks, had it's own libuv-based event loop, etc. Also, it had garbage collection built into the language.
These were removed before 1.0, which made Rust a lot better as a systems language: you could reliably embed it into non-Rust programs, you could reliably interoperate with non-Rust libraries that didn't expect to be moved around threads, you didn't need to care about starting up the GC (or handling GC pauses), etc.
This was a good decision for Rust, and it turns out most of the things people wanted to do with these features could be done outside it - e.g., the borrow checker avoided the need for pervasive GC. (Though almost certainly not intentional, one side effect is that it distinguished Rust from Go: Go is great for standalone programs that need lightweight concurrency, but it's very bad at being embedded into other code and not the best choice if you're mostly calling FFI libraries.)
First, it would be good for Rust to stick to that decision. Rust should not regain a pausing GC in the standard library - similarly, it should not regain a thread manager in the standard library.
Second, it would be good for Rust libraries to work within the spirit of that decision. That's a lot harder, because part of the expectation when those features were removed was that some needs - notably around event-based processing - would be met by third-party libraries. As I understand it (and I might be totally wrong!), it was honestly a bit of luck that the borrow checker worked as well as it did and was ready at the right time, and the expectation was that someone would add a GC library and it would be widely used. However, it's very good that no widely-used GC library sprung up. In the same vein, it would be good for there to be no widely-used third-party thread manager library.
This might be hard, possibly requiring a borrow-checker-level miracle, but it's worth aiming for. And if there has to be a thread manager (or a garbage collector), it should not be part of core Rust.
Would you agree with that phrasing?
--
Incidentally, why does reqwest start up a polling thread when called in sync mode? Can't it do the polling on the main thread? (Or in other words, async programming doesn't imply multithreaded programming. I actually sort of expect that async programming is better suited to single-threaded programming with an event loop, because if you're okay with threads, you may as well just write synchronous code on threads! So either there is something subtle and very interesting here, or there's an easy fix, or I'm misunderstanding something badly.)
- dunkelheit 6y agoI think this rephrasing misses the mark a bit on the original concern. GP explicitly states that he wants Rust-the-language to remain as it is - close to the metal, no GC, with minimal runtime and 1:1 threading. The concern is indeed with the libraries/ecosystem. We are not quite there yet, but it is not hard to imagine that in a few years somebody who asks how to do some simple task in a blocking fashion will be met with replies in the vein of "well, that's not idiomatic", "why don't you use async", or "there was a library for that but it is now kind of unmaintained". All because the main effort of the community went into supporting and maintaining the async mode. If the reqwest library indeed spawns a thread for every request, it is a good example of that dynamic. Sync mode kind of works but is clearly a second-class citizen and works in a suboptimal fashion. And if you want to peek under the hood to debug it you still have to deal with the async machinery in all its gory detail.
- geofft 6y agoSo that gets at my second question. I would expect that if async mode is working well, it specifically avoids needing libraries to spawn a thread. Or put another way - when Rust removed M:N threading and also shipped out of the box with no event handling support after removing librustuv, the recommendation was that libraries should use threads to handle concurrency and make blocking calls on each thread, and modern OSes make threads perform well, so why not. Isn't the whole point of revisiting async to avoid that answer? I have the same use cases of wanting Rust to be a close-to-the-metal language with a minimal runtime that you can safely plop in place of any C code, and it seems to me that the way to do that is to get the async story to be so good that people start saying "Well, that's not idiomatic" and "That approach was common but the libraries are all unmaintained" to libraries that spawn threads. What am I missing? Why are we associating "more async" with "more threads" instead of "remain on the calling thread and use an event loop"?