Creating Our Own Fibers 116 The first thing we do is to implement a new function on our Runtime struct: impl Runtime { pub fn new()-> Self { let base_thread = Thread { stack: vec![0_u8; DEFAULT_STACK_SIZE], ctx: Thread Context::default(), state: State::Running, }; let mut threads = vec![base_thread]; let mut available_threads: Vec<Thread> = (1..MAX_THREADS). map(|_| Thread::new()). collect(); threads. append(&mut available_threads); Runtime { threads, current: 0, } } When we instantiate our Runtime, we set up a base thread. This thread will be set to the Running state and will make sure we keep the runtime running until all tasks are finished. Then, we instantiate the rest of the threads and set the current thread (the base thread) to 0. The next thing we do is admittedly a little bit hacky since we do something that's usually a no-go in Rust. As I mentioned when we went through the constants, we want to access our runtime struct from anywhere in our code so that we can call yield on it at any point in our code. There are ways to do this safely, but the topic at hand is already complex, so even though we're juggling with knives here, I will do everything I can to keep everything that's not the main focal point of this example as simple as it can be. After we call initialize on the Runtime, we have to make sure we don't do anything that can invalidate the pointer we take to self once it's initialized. pub fn init(&self) { unsafe { let r_ptr: *const Runtime = self; RUNTIME = r_ptr as usize; } }