Similarities between C# async/await and Tasks and Rust async/await and Futures?

I am currently getting to do with C# async/await and Tasks at work while writing an interface between a native C++ DLL driver we provide and the client's C# code. However, I don't really have a good mental model of what's going on with async/await. I get a warning that my async function will run synchronously.

For the moment it's fine; both sides are still in beta development. However, I'd really like to fix that warning for release.

The reason I'm posting here, is that I want to know if it would make sense to dive deep into Rust async/await and Futures to get a grasp on C#'s equivalents? Since I'm going to be on leave for 3 weeks soon, I'd like to play around with the concepts during that time. However, I've been meaning to get into Rust async/await as well for a long time. Can I kill two birds with one stone?

I'm afraid that Rust's Futures and C#'s tasks aren't so similar. They both have the async and await keywords, and are intended for asynchronous workflows, but the similarities sort of end there.

C# tasks are 'hot', in that they progress to their first await point as part of the call; Rust's Futures do not, they don't advance at all when first 'called' - until they're scheduled (which is a more manual process in Rust), they won't do anything at all.

C# tasks are usually scheduled for you by the runtime - it's a really complicated area, but if you're working in C# GUI applications, this complexity is mostly just handled for you. In Rust, you generally schedule tasks yourself, with an executor. There are some nice abstractions that make this less daunting than it sounds, but as with most things in Rust, the complexity and tradeoffs are much closer to the surface.

To quickly get a little off-topic for the forum, but more directly address your initial confusion: As I mentioned earlier, C# Tasks are hot, they execute until their first internal await when they are called. The 'async function will run synchronously' warning is letting you know that you don't have any await points in that function. This can be okay, particularly if in the future you may need to add awaits in the function, since it'd be a breaking change to change the return type from some T to Task<T> (and tag the function async).

I hope this clarifies things a little bit. Async is really cool tech, and very useful, but it's a deep and nuanced topic, like many other areas. Good luck!

9 Likes