Live data from Hacker News

Building a shared vision for Async Rust

blog.rust-lang.org

101–110 of 139 posts

Re: Building a shared vision for Async Rust

#101
post #73

Earlier quoted context omitted.

I'm not sure exactly what you're asking, but I think it's either answered by the "What color is your function?" article I linked above, or by the answer that this is exactly why unasync exists and why I suggested that approach is worth considering, or by the answer that you can, in fact, just run the function and the "bloat" (which is just syntactic bloat - note that performance is generally going to be better!) is t…

yes sorry, those were rhetorical questions. Your point about asking you fail to see why not using a blocking executor to deal with the async code. My problem is with needing the executor at all. I must have skipped a couple of you previous pints in this thread. Apologies about that... Maybe we should start trying to think about async as being something can use if they want and ignore if they want. Code being async co…

How would this work? (I do really think this is the right model, I'm just trying to figure out what that model is, exactly. :) )

Let's say I have code like this, in Python asyncio:

    class ShardedDBClient:
        async def query(self, key):
            tasks = [self.query_shard(key) for shard in self.shards]
            results = await asyncio.gather(*tasks)
            for partial_result in results:
                if key in partial_result:
                    return partial_result[key]
            return None
How do you run this without an executor?

The obvious way to make it not be "async required" is to say, we get rid of the async/await keywords - but what do you do with that "await asyncio.gather" instruction? Do you call each of those callbacks serially?

Generally, even in Rust (perhaps especially in Rust), I would expect this to use some OS facility for waiting on multiple sockets (possibly even just boring select(), but preferably epoll/kqueue) to send a bunch of database requests out in parallel and then wait on all their sockets to handle responses as they arrive. I would expect that even if my own code doesn't involve async/await at all.

The easy way to implement that is

        def sync_query(self, key):
            return asyncio.run(self.query(key))
which creates an asyncio executor just to run that one function.

This is going to be a lot faster than querying those shards one at a time! And it also can semantically change how the library behaves - imagine that there's a timeout parameter, and I set a 100ms timeout. I probably mean that to be 100ms for the entire operation, not 100ms per request, but I probably also don't expect my calls to always fail if each query takes 10ms and there are more than 10 shards.

The downside is that this library is quietly using asyncio without you knowing. But how exactly is that a downside? I already expect the library to be using select/epoll/kqueue without me knowing. And in a language like Rust, the executor should basically compile out - it should be a "zero-cost abstraction" compared to writing the event-handling code by hand.

Re: Building a shared vision for Async Rust

#102
post #81

Earlier quoted context omitted.

"async/await" is just syntactic sugar for a function that returns a Future plus a state machine at yield points. You need a library (executor) to run that Future (execute that function). The Rust standard library's block_on uses a global ThreadPool, and the docs recommend using a LocalPool if you need finer grained control. So, to answer your question it depends on the executor (the thing that implements block_on).

(To be clear, block_on is not in the Rust standard library.)

ah, darn, thanks!

So for anyone reading for the correct details: the block_on I was thinking of is part of the futures crate, which is not in std, it's not an "official library".

Re: Building a shared vision for Async Rust

#103
post #41

Earlier quoted context omitted.

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 reliabl…

I 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…

So 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"?

Re: Building a shared vision for Async Rust

#104
post #8

I admire the passion, but I’m not sure why I would want Rust at all, not just async. When I want memory safety, async-await, I/O performance, easy multithreading, I write C#. When I want performance of CPU bound code, or lots of integration with native libraries/APIs, I write C++. Sometimes I want both in the same software, compile C++ code into a DLL (or shared library on Linux), and consume it from C#. I don’t have…

I would not think to consider C# outside of Windows, but maybe I am wrong and should update my priors. Are there any C# programs that are regularly used ex-Windows? Something analogous to Docker (written in Go) or ripgrep (written in Rust)?

Technically, some from these lists: https://en.wikipedia.org/wiki/List_of_Unity_games https://dotnet.microsoft.com/apps/xamarin/customers However, the runtimes are unusual there, neither Unity nor Xamarin run traditional net-core CLR, they still use Mono, for some platforms even AOT compiled.

About normal .NET core, probably asp.net is the most known. stackoverflow.com have recently migrated to aspnet-core but I’m not sure if they migrated from Windows Server yet.

Re: Building a shared vision for Async Rust

#105
post #7

Is there any thought to including a default executor in the standard library? I think it's kind of an obstacle for beginners when the language/stdlib provide all the tools to write async code, but not to run it. I saw discussed in this talk the intent in allowing developers to provide their own executor based on the specifics of their use case: https://youtu.be/NNwK5ZPAJCk?t=1107 This makes sense to me; however, I fe…

I know lots of Rust newcomers have an expectation that the standard library should be all they need, and dependencies should be avoided, but that's not Rust. In Rust dependencies are good, you're supposed to use them. If something can be implemented well outside of std, it should probably remain outside std.

The problem is that a standard library is a heavy burden for a language, and a huge risk for its longevity (think 40 years from now). It promises that the first stable release of any feature will work forever, and never change. It's an unrealistic promise for anything non-trivial.

In other languages it often played out like this:

1. std added a feature,

2. it turned out that it wasn't the best API, but it couldn't be fixed,

3. people fed up with the poor std API wrote a replacement,

4. everyone has to be reminded "don't use the std version, use the replacement instead" forever.

Rust jumps straight to the point 4.

Re: Building a shared vision for Async Rust

#106
post #102

Earlier quoted context omitted.

(To be clear, block_on is not in the Rust standard library.)

ah, darn, thanks! So for anyone reading for the correct details: the block_on I was thinking of is part of the futures crate, which is not in std, it's not an "official library".

It's all good! It was proposed, but decided to have an RFC first. Your point is still correct, just wanted to make sure to clarify this detail :)

Re: Building a shared vision for Async Rust

#107
post #8

I admire the passion, but I’m not sure why I would want Rust at all, not just async. When I want memory safety, async-await, I/O performance, easy multithreading, I write C#. When I want performance of CPU bound code, or lots of integration with native libraries/APIs, I write C++. Sometimes I want both in the same software, compile C++ code into a DLL (or shared library on Linux), and consume it from C#. I don’t have…

Downplaying Rust isn’t going to go over well in this thread. I happen to completely agree with you, but I would add or say that Rust is the first new programming language in a very long time that qualifies as technical innovation and not technical churn. Maybe the only one since Java. And I'm a C# guy, I wouldn't want to use anything else, it's Java done right. But standing back and looking at the big list[0], only C…

> Downplaying Rust isn’t going to go over well in this thread.

People were downvoting the OP not because they were criticizing Rust, but they were off-topic. The article is about Rust async; OPs comment is about Rust.

Re: Building a shared vision for Async Rust

#108
post #91

Earlier quoted context omitted.

Why not? The fix there sounds like it's as simple as adding some yield points.

Yield points in the middle of a large matrix multiplication (for example)? Manually scheduling threads seems like a really shitty way to program

Not really? That's cooperative multitasking and it's used a lot: https://en.wikipedia.org/wiki/Cooperative_multitasking

But regardless, the GP post was not taking about matrix math, it seems it was talking about sending an HTTP request and waiting for a response, which is something that actually is I/O bound on the TCP socket.

Re: Building a shared vision for Async Rust

#109
post #28

Earlier quoted context omitted.

Your comment touches on a few misconceptions I see a lot. Firstly, `reqwest` exposes both an async and a synchronous API, allowing the developer to choose which one to use. They are largely interchangeable code-wise. [1] Secondarily, and more broadly, async is possible to opt out of. You must understand that most web and network related libraries will be async by default for performance, because people who write in R…

Complexity kills code. Being able to reason about what your code is doing, is FAR more valuable to me than async. Having tokio act as my runtime and switch tasks as it sees fit will be debug hell. The problem I see is the current async story is opt-out. It's use async or go find something else. Async should be opt in. As in, the code works regardless of an async runtime, async is added magic if you want it, but it wi…

> The problem I see is the current async story is opt-out.

Surely this is only the case if you have picked asynchronous libraries to use?

Re: Building a shared vision for Async Rust

#110

Earlier quoted context omitted.

Complexity kills code. Being able to reason about what your code is doing, is FAR more valuable to me than async. Having tokio act as my runtime and switch tasks as it sees fit will be debug hell. The problem I see is the current async story is opt-out. It's use async or go find something else. Async should be opt in. As in, the code works regardless of an async runtime, async is added magic if you want it, but it wi…

> The problem I see is the current async story is opt-out. Surely this is only the case if you have picked asynchronous libraries to use?

Yes, in fact, you cannot even use async Rust without writing your own executor or bringing one in via a library. It is very, very much opt in. That was a hard constraint on the design.

However, I think what the parent is getting at is the feeling of the total package, not the technical details. If every library you want to use is async, you can't really "opt out" exactly, even if technically the feature is opt out.

Post reply on HN