Creating Your Own Runtime 266 In practice, that means that Isahc brings its own reactor since it comes with the machinery to store wakers and call wake on them when an operation is ready. The reactor is started implicitly. Incidentally, this is also one of the major differences between async_std and Tokio. Tokio requires explicit instantiation, and async_std relies on implicit instantiation. I'm not going into so much detail on this just for fun; while this seems like a minor difference, it has a rather big impact on how intuitive asynchronous programming in Rust is. This problem mostly arises when you start programming using a different runtime than Tokio and then have to use a library that internally relies on a Tokio reactor being present. Since you can't have two Tokio instances running on the same thread, the library can't implicitly start a Tokio reactor. Instead, what often happens is that you try to use that library and get an error like we did in the preceding example. Now, you have to solve this by starting a Tokio reactor yourself, use some kind of compatibility wrapper created by someone else, or seeing whether the runtime you use has a built-in mechanism for running futures that rely on a Tokio reactor being present. For most people who don't know about reactors, executors, and different kinds of leaf futures, this can be quite unintuitive and cause quite a bit of frustration. Note The problem we describe here is quite common, and it's not helped by the fact that async libraries rarely explain this well or even try to be explicit about what kind of runtime they use. Some libraries might only mention that they're built on top of Tokio somewhere in the README file, and some might simply state that they're built on top of Hyper, for example, assuming that you know that Hyper is built on top of Tokio (at least by default). But now, you know that you should check this to avoid any surprises, and if you encounter this issue, you know exactly what the problem is. Ergonomics versus efficiency and flexibility Rust is good at being ergonomic and efficient, and that almost makes it difficult to remember that when Rust is faced with the choice between being efficient or ergonomic, it will choose to be efficient. Many of the most popular crates in the ecosystem echo these values, and that includes async runtimes. Some tasks can be more efficient if they're tightly integrated with the executor, and therefore, if you use them in your library, you will be dependent on that specific runtime. Let's take timers as an example, but task notifications where Task A notifies Task B that it can continue is another example with some of the same trade-offs.