4 ms·
Question: How much of Rust’s type system complexity is due to async? It seems Pin is mostly related to async and the official examples for GAT are for async. G
by RcouF1uZ4gsC 4y ago
Question: How much of Rust’s type system complexity is due to async? It seems Pin is mostly related to async and the official examples for GAT are for async.
Given the complexities of async and how other alternatives such a Java Loom’s virtual threads, in about 10 years I am not sure that we won’t see Rust going all in on async as a mistake.
- 2fast4you 4y agoSince we’re comparing Go and Rust, I think Go really nailed it with goroutines and channels. It’s much easier to get your head around than async.
- deleted 4y ago[deleted]
- afdbcreid 4y agoI don't think this is right. To me at least JS's promises and C#'s async/await are as easy as Go's goroutines. The problem with Rust is the effort to make everything static and without heap allocations, which is understandable but enforce really hard-to-grasp design. On the other hand, you can get rid of most issues of async even in Rust by boxing.
- toast0 4y ago> I am not sure that we won’t see Rust going all in on async as a mistake. I'm about a month into Rust, but Rust didn't go all in to async; it only went halfway in, which is why some functions are async and others aren't. Erlang is all in on async/green threads/tasks. From what I gather, so is Loom. There's no special way to write async code, you just write regular code and it works; for Loom you just spawn your threads a little differently. For Rust, you have to be somewhat careful about what you do or don't do in a task, and you have to write .await everywhere.
- sph 4y agoErlang isn't async. The actor model is great because you only have to think in single threaded mode. And with immutability being a core language feature, data races and other concurrency issues are literally impossible.
- toast0 4y agoIt's got async messaging, so it's not not async either. It's also got the trick that every function call is a potential premption point, and immutability means you can't go very long between function calls.
- eloff 4y agoYes, I think async is where a lot of the complexity comes in.
- pornel 4y agoThe GAT complexity is due to async working without extra per-task allocations. If you allowed a heap allocation per each Future (like e.g. JS does for each Promise), it'd be pretty simple, but Rust goes to great lengths to avoid heap allocations. Rust can merge the entire call tree of async execution (which can be as large as the whole program if async recursion isn't used) into one struct that represents entire state space needed from start to finish. This struct has a static type and a fixed size known at compile time. Describing that type in the type system, including all the generics and lifetimes, is pretty complex. Pin is a bit of a technical debt. It has been invented to ship async/await without having to first add non-moveable/self-referential types to the language. It is more clever and clunky than a first-class language feature could be. OTOH async/await has been production-ready for a few years now, and non-moveable types are still not a thing, so the trade-off was worth it. In practice you don't need to even know about Pin if you're just using async/await syntax. It's needed only for lower-level plumbing, like custom runtimes or clever low-level primitives that can't be expressed in normal async code.