Zaplib / zaplib

⚡ Zaplib is an open-source library for speeding up web applications using Rust and WebAssembly.
https://zaplib.com
Apache License 2.0
1.26k stars 37 forks source link

Allow pointers to arbitrary data structures in Rust #125

Open janpaul123 opened 2 years ago

janpaul123 commented 2 years ago

We should be able to send a Box<T> (mutable) or an Arc<T> (immutable) to JS. On the JS side you can't do anything with it, except send it back to Rust, or send it between Web Workers.

This is useful when doing complicated things in Rust, while the "locus of control" is still in Javascript.

The particular use case for Flux involves having a matrix that we don't need to read of modify directly on the JS side, so we want flexibility on how to represent it in Rust (e.g. swap out one matrix library for another).

stevekrouse commented 2 years ago

From the Rust side, it would look like this:

operation1() -> Box<T> {
  let data : Box<T> = ...
  return vec![data.into_param()];
}

operation2(input: Vec<ZapParam>) {
  let data = params[0] // how do we cast this into a Box<T> ?
}

From the JS side it'd look like:

const [data] = callRustSync('operation1', [])
callRustSync('operation2', [data])
janpaul123 commented 2 years ago

Yeah exactly. To add some more details, we can call the type of data a ZapPointer perhaps, and we could even add a zaplib.isZapPointer in addition to our zaplib.isZapBuffer. (There are different ways to structure this so this is just one idea)

Re how do we cast this into a Box<T>, I would imagine doing let data = params[0].into_val::<T>() to get a T.

And for the immutable case something similar, but for Arc<T>.

We could even have convenience methods for getting &T or &mut T but that's not as necessary.

janpaul123 commented 2 years ago

Workaround is something like this:

impl MyStruct {
    fn into_param(self) -> ZapParam {
        (Box::into_raw(Box::new(self)) as usize).to_string().into_param()
    }

    fn from_param(param: ZapParam) -> Self {
        *unsafe { Box::from_raw(param.as_str().parse::<usize>().unwrap() as *mut Self ) }
    }
}

Which has some overhead in the conversions from/to strings, plus it leaks memory if you drop the pointer on the JS side.