A mental model of an async runtime 131 This is an example of a non-leaf future: let non_leaf = async { let mut stream = Tcp Stream::connect("127. 0. 0. 1:3000"). await. unwrap(); println!("connected!"); let result = stream. write(b"hello world\n"). await; println!("message sent!"); ... }; The two highlighted lines indicate points where we pause the execution, yield control to a runtime, and eventually resume. In contrast to leaf futures, these kinds of futures do not themselves represent an I/O resource. When we poll them, they will run until they get to a leaf future that returns Pending and then yields control to the scheduler (which is a part of what we call the runtime). Runtimes Languages such as C#, Java Script, Java, Go, and many others come with a runtime for handling concurrency. So, if you're used to one of those languages, this will seem a bit strange to you. Rust is different from these languages in the sense that Rust doesn't come with a runtime for handling concurrency, so you need to use a library that provides this for you. Quite a bit of complexity attributed to futures is actually complexity rooted in runtimes; creating an efficient runtime is hard. Learning how to use one correctly requires quite a bit of effort as well, but you'll see that there are several similarities between this kind of runtime, so learning one makes learning the next much easier. The difference between Rust and other languages is that you have to make an active choice when it comes to picking a runtime. Most often, in other languages, you'll just use the one provided for you. A mental model of an async runtime I find it easier to reason about how futures work by creating a high-level mental model we can use. To do that, I have to introduce the concept of a runtime that will drive our futures to completion. Note The mental model I create here is not the only way to drive futures to completion, and Rust's futures do not impose any restrictions on how you actually accomplish this task.