Creating Your Own Runtime 268 Let's take spawning as an example. When you write a high-level async library in Rust, such as a web server, you'll likely want to be able to spawn new tasks (top-level futures). For example, each connection to the server will most likely be a new task that you want to spawn onto the executor. Now, spawning is specific to each executor, and Rust doesn't have a trait that defines how to spawn a task. There is a trait suggested for spawning in the future-rs crate, but creating a spawn trait that is both zero-cost and flexible enough to support all kinds of runtimes turns out to be very difficult. There are ways around this. The popular HTTP library Hyper ( https://hyper. rs/ ), for example, uses a trait to represent the executor and internally uses that to spawn new tasks. This makes it possible for users to implement this trait for a different executor and hand it back to Hyper. By implementing this trait for a different executor, Hyper will use a different spawner than its default option (which is the one in Tokio's executor). Here is an example of how this is used for async_std with Hyper: https://github. com/async-rs/async-std-hyper. However, since there is no universal way of making this work, most libraries that rely on executor-specific functionality do one of two things: 1. Choose a runtime and stick with it. 2. Implement two versions of the library supporting different popular runtimes that users choose by enabling the correct features. Async drop Async drop, or async destructors, is an aspect of async Rust that's somewhat unresolved at the time of writing this book. Rust uses a pattern called RAII, which means that when a type is created, so are its resources, and when a type is dropped, the resources are freed as well. The compiler automatically inserts a call to drop on objects when they go out of scope. If we take our runtime as an example, when resources are dropped, they do so in a blocking manner. This is normally not a big problem since a drop likely won't block the executor for too long, but it isn't always so. If we have a drop implementation that takes a long time to finish (for example, if the drop needs to manage I/O, or makes a blocking call to the OS kernel, which is perfectly legal and sometimes even unavoidable in Rust), it can potentially block the executor. So, an async drop would somehow be able to yield to the scheduler in such cases, and this is not possible at the moment. Now, this isn't a rough edge of async Rust you're likely to encounter as a user of async libraries, but it's worth knowing about since right now, the only way to make sure this doesn't cause issues is to be careful what you put in the drop implementation for types that are used in an async context. So, while this is not an extensive list of everything that causes friction in async Rust, it's some of the points I find most noticeable and worth knowing about.