Created
April 5, 2018 12:20
-
-
Save Lisoph/04bd09fde346af08e8857ce69c897b6d to your computer and use it in GitHub Desktop.
Coroutine implementation similar to Unity's, in nightly Rust.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(generators, generator_trait)] | |
use std::ops::{Generator, GeneratorState}; | |
use std::thread; | |
use std::time::{Duration, Instant}; | |
struct CoStep; | |
impl CoGenerator for CoStep { | |
fn resume(&mut self) -> CoGenState { | |
CoGenState::Returned | |
} | |
} | |
struct CoWait { | |
start: Instant, | |
duration: Duration, | |
} | |
impl CoWait { | |
fn new(duration: Duration) -> Self { | |
Self { | |
start: Instant::now(), | |
duration, | |
} | |
} | |
} | |
impl CoGenerator for CoWait { | |
fn resume(&mut self) -> CoGenState { | |
if self.start.elapsed() >= self.duration { | |
CoGenState::Returned | |
} else { | |
CoGenState::Yielded(Box::new(CoStep)) | |
} | |
} | |
} | |
enum CoGenState { | |
Yielded(Box<CoGenerator>), | |
Returned, | |
} | |
trait CoGenerator { | |
fn resume(&mut self) -> CoGenState; | |
} | |
impl<T, R> CoGenerator for T | |
where | |
T: Generator<Yield = Box<CoGenerator>, Return = R>, | |
{ | |
fn resume(&mut self) -> CoGenState { | |
match unsafe { <Self as Generator>::resume(self) } { | |
GeneratorState::Yielded(co) => CoGenState::Yielded(co), | |
GeneratorState::Complete(_) => CoGenState::Returned, | |
} | |
} | |
} | |
struct Coroutine<'a> { | |
generators: Vec<Box<CoGenerator + 'a>>, | |
} | |
impl<'a> Coroutine<'a> { | |
fn step(&mut self) -> bool { | |
let state = self.generators.last_mut().map(|gen| gen.resume()); | |
if let Some(state) = state { | |
match state { | |
CoGenState::Yielded(gen) => self.generators.push(gen), | |
CoGenState::Returned => { | |
self.generators.pop(); | |
} | |
} | |
true | |
} else { | |
false | |
} | |
} | |
} | |
fn main() { | |
let mut opacity = 255u8; | |
{ | |
let fade = || { | |
if false { | |
yield (return ()) as Box<CoGenerator>; | |
} | |
let animation_duration = Duration::from_secs(2); | |
let animation_start = Instant::now(); | |
loop { | |
opacity -= 1; | |
println!("Darkened the image"); | |
if animation_start.elapsed() >= animation_duration { | |
break; | |
} else { | |
if opacity == 250 { | |
let annoy_user = || { | |
if false { | |
yield (return ()) as Box<CoGenerator>; | |
} | |
println!("I'll just waste your time for 3 seconds."); | |
yield Box::new(CoWait::new(Duration::from_secs(3))); | |
println!("OK, enough time wasted."); | |
}; | |
yield Box::new(annoy_user); | |
} else { | |
yield Box::new(CoWait::new(Duration::from_millis(100))); | |
} | |
} | |
} | |
println!("Almost done"); | |
yield Box::new(CoStep); | |
println!("Now I'm done"); | |
}; | |
let mut co = Coroutine { | |
generators: vec![Box::new(fade)], | |
}; | |
while co.step() { | |
println!("Step"); | |
thread::sleep(Duration::from_millis(50)); | |
} | |
} | |
println!("Darkened the image for {} values.", 255 - opacity); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment