Introduction to runtimes and why we need them 169 Introduction to runtimes and why we need them As you know by now, you need to bring your own runtime for driving and scheduling asynchronous tasks in Rust. Runtimes come in many flavors, from the popular Embassy embedded runtime ( https://github. com/embassy-rs/embassy ), which centers more on general multitasking and can replace the need for a real-time operating system (RTOS ) on many platforms, to Tok io (https://github. com/tokio-rs/tokio ), which centers on non-blocking I/O on popular server and desktop operating systems. All runtimes in Rust need to do at least two things: schedule and drive objects implementing Rust's Future trait to completion. Going forward in this chapter, we'll mostly focus on runtimes for doing non-blocking I/O on popular desktop and server operating systems such as Windows, Linux, and mac OS. This is also by far the most common type of runtime most programmers will encounter in Rust. Taking control over how tasks are scheduled is very invasive, and it's pretty much a one-way street. If you rely on a userland scheduler to run your tasks, you cannot, at the same time, use the OS scheduler (without jumping through several hoops), since mixing them in your code will wreak havoc and might end up defeating the whole purpose of writing an asynchronous program. The following diagram illustrates the different schedulers: Figure 8. 1-Task scheduling in a single-threaded asynchronous system 