Skip to content

Instantly share code, notes, and snippets.

@Lisoph
Created April 5, 2018 12:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lisoph/04bd09fde346af08e8857ce69c897b6d to your computer and use it in GitHub Desktop.
Save Lisoph/04bd09fde346af08e8857ce69c897b6d to your computer and use it in GitHub Desktop.
Coroutine implementation similar to Unity's, in nightly Rust.
#![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