Earlier quoted context omitted.
Perhaps more accurate to say "safe reclamation of dynamic allocations without GC was not known to be possible in a practical programming language, before Rust". The problem with languages like ATS and Cyclone is that you need heavy usage in real-world applications to prove that your approach is actually usable by developers at scale. Rust achieved that first.
I have always thought Pascal solved that in practise with automated reference counting on arrays A good optimizer could then have removed the counting on non-escaping local variables
Why asynchronous Rust doesn't work
411–420 of 499 posts
Re: Why asynchronous Rust doesn't work
#412Earlier quoted context omitted.
There's a workaround, but it's unidiomatic, requires more traits, and requires inefficient copying of data if you want to adapt from one to the other. However, I wouldn't call this a problem with a polling-based model. At least part of the goal here must be to avoid allocations and reference counting. If you don't care about that, then the design could have been to 'just' pass around atomically-reference-counted buff…
> I am not sure whether this is actually viable. Having investigated this myself, I would be very surprised to discover that it is. The only viable solution to make AsyncRead zero cost for io-uring would be to have required futures to be polled to completion before they are dropped. So you can give up on select and most necessary concurrency primitives. You really want to be able to stop running futures you don't nee…
Having to wait for cancellation does sound expensive, especially if the end goal is to pervasively use APIs like io_uring where cancellation can be slow.
But then, in a typical use of select, you don't actually want to cancel the I/O operations represented by the other futures. Rather, you're running select in a loop in order to handle each completed operation as it comes.
So I think the endgame of this hypothetical world is to encourage having the actual I/O be initiated by a Future or Stream created outside the loop. Then within the loop you would poll on `&mut future` or `stream.next()`. This already exists and is already cheaper in some cases, but it would be significantly cheaper when the backend is io_uring.
Re: Why asynchronous Rust doesn't work
#413Earlier quoted context omitted.
Please, calm down. I do appreciate your work on Rust, but people do make mistakes and I strongly belive that in the long term the async stabilization was one of them. It's debatable whether async was essential or not for Rust, I agree it gave Rust a noticeable boost in popularity, but personally I don't think it was worth the long term cost. I do not intend to change your opinion, but I will keep mine and reserve the…
> Please, calm down. By the way, that will almost certainly be taken in a bad way. It's never a good idea to start a comment with something like "chill" or "calm down", as it feels incredibly dismissive. > I do appreciate your work on Rust, but There's a saying that anything before a "but" is meaningless. This is not meant to critique the rest of the comment, just point out a couple parts that don't help in defusing…
I have noticed this comment only after engaging with him in https://news.ycombinator.com/item?id=26410565 in which he wrote about me:
> You do not know what you are talking about.
> You are confused.
So my reaction was a bit too harsh partially due to that.
Re: Why asynchronous Rust doesn't work
#414Earlier quoted context omitted.
Rust programs can theoretically be fast, but most of the ones I've used are slow. I tried two high profile implementations of the same type of software, one in Rust and one in Java. The Java one was faster and used less memory. Rust programmers tend to do all kinds of little hacks here and there to make the borrow checker happy. It can add up. The borrow checker is perfectly happy when you copy everything. Rust is be…
> Rust programmers tend to do all kinds of little hacks here and there to make the borrow checker happy. It can add up. The borrow checker is perfectly happy when you copy everything. Do you mind sharing an example? If you're talking about using to_owned or clone without reason, then it's fully on the developer. Some more pitfalls to avodi: https://llogiq.github.io/2017/06/01/perf-pitfalls.html What you say definitel…
Re: Why asynchronous Rust doesn't work
#415All that said, the author's article reads as a bit daft. I think anyone who has tried building something complicated in C++ / Go will look at those examples and marvel at how awesome Rust's ability to understand lifetimes is (i.e. better than your own) and keep you from using resources in an unintended way. E.g., you want to keep some data alive for a closure and locally? Arc. Both need to writable? Arc Mutex. You are a genius and can guarantee this Fn will never leak and it's safe to have it not capture something by value that is used later in the program and you really need the performance of not using Arc? Cast it to a ptr and read in an unsafe block in the closure. Rust doesn't stop you from doing whatever you want in this regard, it just makes you explicitly ask for what you want rather than doing something stupid automatically and making you have a hard to find bug later down the line.
Re: Why asynchronous Rust doesn't work
#416As I read through the database example, I saw that the compiler just caught a multi-threading bug for the author, and instead of being thankful, he’s complaining that Rust is bad. I think he should use a higher level framework, or wait a few years for them to mature, and use a garbage collected language until then.
> caught a multi-threading bug The compiler complained: “error[E0308]: mismatched types”
Re: Why asynchronous Rust doesn't work
#417Earlier quoted context omitted.
This post is completely and totally wrong. At least you got to ruin my day, I hope that's a consolation prize for you. There is NO meaningful connection between the completion vs polling futures model and the epoll vs io-uring IO models. comex's comments regarding this fact are mostly accurate. The polling model that Rust chose is the only approach that has been able to achieve single allocation state machines in Rus…
I've jumped on the Rust bandwagon as part of ZeroTier 2.0 (not rewriting its core, but rewriting some service stuff in Rust and considering the core eventually). I've used a bit of async and while it's not as easy as Go (nothing is!) it's pretty damn ingenious for language-native async in a systems programming language. I personally would have just chickened out on language native async in Rust and told people to rol…
where F: Fn() -> Fut, Fut: Future
i.e. you call some closure f that returns a future that you can then await on. when writing that out it will look like: || {
// closure
async move {
// returned future
}
}
`make_service_fn` likely takes something like this and puts it in a struct, then for every request it will call the closure to create the future to process the request. (edit: and indeed it does, it's definition literally takes your closure and uses it to implement the Service trait, which you are free to do also if you didn't want to write it this way https://docs.rs/hyper/0.14.4/src/hyper/service/make.rs.html#...)The reason you need to clone in the closure is that is what 'closes over' the scope and is able to capture the Arc reference you need to pass to your future. Whenever make_service_fn uses the closure you pass to it, it will call the closure, which can create your Arc references, then create a future with those references "moved" in.
It's a little deceptive as this means the exact same thing as above, just with the first set of curly braces not needed
|| async move {}
This is still a closure which returns a Future. Does all of that make sense? Perhaps they could use a more explicit example, but it also helps to carefully read the type signature.Re: Why asynchronous Rust doesn't work
#418As I already mentioned multiple times, the future are tracing GCs with an ownership concept for niche use cases. Rust's ideal use case are the scenarios that use stuff like MISRA-C, kernel and drivers code. The design team has done a wonderful work pushing for affine types into mainstream, however outside of forbidden-GC scenarios, the productivity loss versus any kind of automatic memory management approach, isn't w…
Deterministic destruction is obviously very useful for resource management, and not just files and database handles, but also locks and scope guards.
Single ownership allows working with mutable data without dangers of shared mutability. Without it you either need true immutability, or defensive coding by proactively copying inputs to ensure nobody else can unexpectedly mutate data you have a reference to.
Single ownership also happens to nicely enforce interfaces that require functions to be called only once or that objects can't be used in invalid state, e.g. after a call to .deinit() or after intermediate steps in builders/fluent interfaces/iterator chains.
Re: Why asynchronous Rust doesn't work
#419Earlier quoted context omitted.
What you've described with pthread_create is basically a closure, just implemented within the limitations of C and like many things in C, wildly unsafe. You can of course interact with the underlying implementation if you want (Rust is quite capable of calling into libc directly), but then you're responsible for ensuring safety. Rust provides a safe wrapper over this with a closure which is generally easier to use an…
Thanks. > a closure which is generally easier to use I didn't experimented much with closures so I can't discuss about simplicity yet (but the article explains that "Closures can be pretty radioactive" ). > within the limitations of C Threads are exposed like that by the operating system. The C implementation is just a way to have access to what the operating system exposes, so I think it should be more like a limita…
In rust and C++ you can make a function with associated state easily and pass it around, both as a raw value with a unique type (though it's difficult to name), and as a reference or heap-allocated value which can be handled generically.
In all these cases you need to make sure that any pointers remain valid until the function has finished executing. In C and C++ the compiler cannot help you, in rust the compiler can catch cases where function could be called after those pointers are no longer valid. This is what causes the 'radioactive' property of closures containing references to other data (they are also radioactive in C++, just at runtime and unpredictably, as opposed to at compile time in rust).
If the example code in the article was written in C++ (and the complaint about the closure type which is also present in C++ was addressed), there would be a subtle concurrency bug, because the database handle could be closed and freed before the closure function actually executes.
In all these cases the solution is either making sure the closure runs before the data it needs becomes invalid (which is not generally easy to do if you're planning to use it to spawn a thread), or you move the data onto the heap, either arranging so the closure frees the data when it executes (and then guaranteeing that it executes), or using some shared reference count system to ensure it only gets freed when all code which might want to use it is done. In Rust and C++ there are mechanisms in the standard library and their closure implementations to achieve this, in C you must do it manually.
Re: Why asynchronous Rust doesn't work
#420Earlier quoted context omitted.
The Rust community regards the JS community with contempt? Where does that opinion come from?
Rust was often touted as a superior alternative to Node.js writing servers. Perhaps this element of contempt is no longer there but it certainly was there before due to the competition. Similar competition existed between Node.js and Golang and there was similar contempt from Go community.
Async in Rust uses a radically different model than JS or golang, but that's not to say the other languages are bad. It's because Rust has different priorities, and needed an async model that works well without a garbage collector.