I'm a rust beginner, and I'm trying to learn by building a simple stack-based VM in rust and adding support for async functions. My idea was to use rust's async functions and just push the future to the execution stack, and when awaiting, just pop the future from the stack, await it, and push the result back to the stack. I managed to do so, but when implementing join_all
function, the code seems more complicated than it needs to be.
Basically, my problem is converting Vec<Rc<RefCell<Pin<Box<dyn Future<Output=Value>>>>>>
to Vec<Pin<&mut dyn Future<Output=Value>>>
Here is the short version of the code:
use std::cell::{RefCell, RefMut};
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::time::Duration;
use async_std::task;
use futures::executor::block_on;
use futures::future::join_all;
/**
* Example async function
*/
pub async fn test_async() -> Value {
task::sleep(Duration::from_millis(1000)).await;
println!("Hello from async");
Value::Void
}
#[derive(Debug)]
enum Instruction {
// Other instructions
CallNative(String),
Await,
Join(usize),
}
#[derive(Clone)]
#[allow(dead_code)]
pub enum Value {
I32(i32),
I64(i64),
F32(f32),
F64(f64),
Bool(bool),
Void,
// Other types
Future(Rc<RefCell<Pin<Box<dyn Future<Output=Value>>>>>),
}
async fn async_main() {
let program = vec![
Instruction::CallNative(stringify!(test_async).to_string()),
Instruction::Await,
Instruction::CallNative(stringify!(test_async).to_string()),
Instruction::Await,
Instruction::CallNative(stringify!(test_async).to_string()),
Instruction::CallNative(stringify!(test_async).to_string()),
Instruction::CallNative(stringify!(test_async).to_string()),
Instruction::CallNative(stringify!(test_async).to_string()),
Instruction::Join(4),
];
let mut stack: Vec<Value> = vec![];
for instruction in program {
match instruction {
Instruction::CallNative(name) => {
let future = match name.as_str() {
stringify!(test_async) => test_async(),
_ => panic!("Unknown native function"),
};
let future: Rc<RefCell<Pin<Box<dyn Future<Output=Value>>>>> = Rc::new(RefCell::new(Box::pin(future)));
stack.push(Value::Future(future));
}
Instruction::Await => {
let future = stack.pop().unwrap();
let future = match future {
Value::Future(future) => future,
_ => panic!("Expected future"),
};
let mut future = future.borrow_mut();
let future = future.as_mut();
let value = future.await;
stack.push(value);
}
Instruction::Join(count) => {
// This part seems more complicated than it should be
let mut futures: Vec<Rc<RefCell<Pin<Box<dyn Future<Output=Value>>>>>> = vec![];
for _ in 0..count {
let future = stack.pop().unwrap();
match future {
Value::Future(future) => futures.push(future),
_ => panic!("Expected future"),
}
}
let futures2: Rc<RefCell<Vec<RefMut<Pin<Box<dyn Future<Output=Value>>>>>>> = Rc::new(RefCell::new(vec![]));
for i in 0..count {
futures2.borrow_mut().push(futures[i].borrow_mut());
}
let mut futures3: Vec<Pin<&mut dyn Future<Output=Value>>> = vec![];
let mut futures2 = futures2.borrow_mut();
for i in futures2.iter_mut() {
futures3.push(i.as_mut());
}
join_all(futures3).await;
}
}
}
}
fn main() {
block_on(async_main());
}