Passing mutable struct to JavaScript callback and keep modifications in WebAssembly - rust

I'm trying to pass a struct from Rust to a JavaScript callback with wasm_bindgen. The JavaScript code should be able to call methods defined in Rust.
This is why this guide doesn't work in my case, as there only objects are passed (you cannot call methods defined in Rust on them), not with references to the actual object in "Rust-Memory".
So basically I have a SomeStruct in Rust:
#[wasm_bindgen]
#[derive(Debug, Clone, Serialize, Deserialize)]
// Note that SomeStruct must not implement the Copy trait, as in the not-minimal-example I have Vec<>s in the struct
pub struct SomeStruct {
pub(crate) field_to_be_modified: i32,
}
#[wasm_bindgen]
impl SomeStruct {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
set_panic_hook();
Self {
field_to_be_modified: 0,
}
}
pub fn modify_field(&mut self, value: i32) {
self.field_to_be_modified = value;
}
pub fn field(&self) -> i32 {
self.field_to_be_modified
}
#[wasm_bindgen]
pub async fn with_callback(&self, function_or_promise: JsValue) -> Result<JsValue, JsValue> {
let mut s = SomeStruct::new();
let function = function_or_promise.dyn_into::<Function>().map_err(|_| {
JsError::new("The provided callback is not a function. Please provide a function.")
})?;
run_any_function(&mut s, function, vec![JsValue::from(1u32)]).await
}
}
However, in order to turn SomeStruct into a JsValue I can pass to JavaScript, I need to clone it first (otherwise the JsValue::from call doesn't compile):
let args = Array::new();
// This is the reason modifications from JS aren't reflected in Rust, but without it JsValue::from doesn't work
let clone = my_struct.clone();
// my_struct is the first function argument
// TODO: JsValue::from only works when cloned, not on the original struct. Why?
// Best would be directly passing my_struct, as then modifications would work
// Passing a pointer to the struct would also be fine, as long as methods can be called on it from JavaScript
args.push(&JsValue::from(clone));
for arg in arguments {
args.push(&arg);
}
// Actually call the function
let result = function.apply(&JsValue::NULL, &args)?;
// TODO: How to turn result back into a SomeStruct struct?
// Copying fields manually also doesn't work because of borrow checker:
// my_struct.field_to_be_modified = clone.field_to_be_modified;
Now I want to modify the field_to_be_modified within the callback passed to with_callback, so I'm calling s_instance.modify_field(42); from JavaScript:
import * as mve from './pkg/mve.js';
async function run() {
let module = await mve.default();
let s = new mve.SomeStruct();
console.log("Initial value (should be 0):", s.field());
await s.with_callback(function(s_instance, second_arg, third_arg) {
// s_instance is of type SomeStruct, and is a COPY of s
console.log("callback was called with parameter", s_instance, second_arg, third_arg);
console.log("Current field value (should be 0):", s_instance.field());
console.log("Setting field to 42");
// This only modifies the copy
s_instance.modify_field(42);
console.log("Field value after setting (should be 42):", s_instance.field());
console.log("end callback");
// TODO: Directly calling methods on s also does not work either
// Error: recursive use of an object detected which would lead to unsafe aliasing in rust
//
// s.modify_field(43);
})
console.log("This should be after \"end callback\"");
// TODO: the original s is unchanged, so
// this does not work, as the callback operated on the cloned s
// TODO: How to make this work?
console.log("Field value after callback (should be 42):", s.field());
}
run();
This callback obviously only changes the cloned value that was passed to it, so the final s.field() call yields 0. However, I cannot
read from the cloned value in Rust after the function is called because of borrow checker errors
pass the actual object I want to modify, as only a copy works
So the question is how I can modify SomeStruct in JavaScript and get it back in Rust. I don't care whether it's unsafe or not, I just want it to work.
I also created a minimum example with the code from this question so you can quickly run it, see the Git repo:
git clone https://github.com/xarantolus/rust-wasm-bindgen-stackoverflow-question
Clone the repo, follow the installation steps for wasm-pack and execute the following to see the problem on your machine:
wasm-pack build --target web && python3 -m http.server
I have also opened an issue for this problem.

Related

How do I define the lifetime for a tokio task spawned from a class?

I'm attempting to write a generic set_interval function helper:
pub fn set_interval<F, Fut>(mut f: F, dur: Duration)
where
F: Send + 'static + FnMut() -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
let mut interval = tokio::time::interval(dur);
tokio::spawn(async move {
// first tick is at 0ms
interval.tick().await;
loop {
interval.tick().await;
tokio::spawn(f());
}
});
}
This works fine until it's called from inside a class:
fn main() {}
struct Foo {}
impl Foo {
fn bar(&self) {
set_interval(|| self.task(), Duration::from_millis(1000));
}
async fn task(&self) {
}
}
self is not 'static, and we can't restrict lifetime parameter to something that is less than 'static because of tokio::task.
Is it possible to modify set_interval implementation so it works in cases like this?
Link to playground
P.S. Tried to
let instance = self.clone();
set_interval(move || instance.task(), Duration::from_millis(1000));
but I also get an error: error: captured variable cannot escape FnMut closure body
Is it possible to modify set_interval implementation so it works in cases like this?
Not really. Though spawn-ing f() really doesn't help either, as it precludes a simple "callback owns the object" solution (as you need either both callback and future to own the object, or just future).
I think that leaves two solutions:
Convert everything to shared mutability Arc, the callback owns one Arc, then on each tick it clones that and moves the clone into the future (the task method).
Have the future (task) acquire the object from some external source instead of being called on one, this way the intermediate callback doesn't need to do anything. Or the callback can do the acquiring and move that into the future, same diff.
Incidentally at this point it could make sense to just create the future directly, but allow cloning it. So instead of taking a callback set_interval would take a clonable future, and it would spawn() clones of its stored future instead of creating them anew.
As mentioned by #Masklinn, you can clone the Arc to allow for this. Note that cloning the Arc will not clone the underlying data, just the pointer, so it is generally OK to do so, and should not have a major impact on performance.
Here is an example. The following code will produce the error async block may outlive the current function, but it borrows data, which is owned by the current function:
fn main() {
// 🛑 Error: async block may outlive the current function, but it borrows data, which is owned by the current function
let data = Arc::new("Hello, World".to_string());
tokio::task::spawn(async {
println!("1: {}", data.len());
});
tokio::task::spawn(async {
println!("2: {}", data.len());
});
}
Rust unhelpfully suggests adding move to both async blocks, but that will result in a borrowing error because there would be multiple ownership.
To fix the problem, we can clone the Arc for each task and then add the move keyword to the async blocks:
fn main() {
let data = Arc::new("Hello, World".to_string());
let data_for_task_1 = data.clone();
tokio::task::spawn(async move {
println!("1: {}", data_for_task_1.len());
});
let data_for_task_2 = data.clone();
tokio::task::spawn(async move {
println!("2: {}", data_for_task_2.len());
});
}

Run async function in run_interval and return result

I need to run an async function in actix::prelude::AsyncContext::run_interval, but I need to also pass in a struct member and return the result (not the future). This is a somewhat more complex version of this question here. As can be seen in the commented section below, I have tried a few approaches but all of them fail for one reason or another.
I have looked at a few related resources, including the AsyncContext trait and these StackOverflow questions: 3, 4.
Here is my example code (actix crate is required in Cargo.toml):
use std::time::Duration;
use actix::{Actor, Arbiter, AsyncContext, Context, System};
struct MyActor {
id: i32
}
impl MyActor {
fn new(id: i32) -> Self {
Self {
id: id,
}
}
fn heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
ctx.run_interval(Duration::from_secs(1), |act, ctx| {
//lifetime issue
//let res = 0;
//Arbiter::spawn(async {
// res = two(act.id).await;
//});
//future must return `()`
//let res = Arbiter::spawn(two(act.id));
//async closures unstable
//let res = Arbiter::current().exec(async || {
// two(act.id).await
//});
});
}
}
impl Actor for MyActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
self.heartbeat(ctx);
}
}
// assume functions `one` and `two` live in another module
async fn one(id: i32) -> i32 {
// assume something is done with id here
let x = id;
1
}
async fn two(id: i32) -> i32 {
let x = id;
// assume this may call other async functions
one(x).await;
2
}
fn main() {
let mut system = System::new("test");
system.block_on(async { MyActor::new(10).start() });
system.run();
}
Rust version:
$ rustc --version
rustc 1.50.0 (cb75ad5db 2021-02-10)
Using Arbiter::spawn would work, but the issue is with the data being accessed from inside the async block that's passed to Arbiter::spawn. Since you're accessing act from inside the async block, that reference will have to live longer than the closure that calls Arbiter::spawn. In fact, in will have to have a lifetime of 'static since the future produced by the async block could potentially live until the end of the program.
One way to get around this in this specific case, given that you need an i32 inside the async block, and an i32 is a Copy type, is to move it:
ctx.run_interval(Duration::from_secs(1), |act, ctx| {
let id = act.id;
Arbiter::spawn(async move {
two(id).await;
});
});
Since we're using async move, the id variable will be moved into the future, and will thus be available whenever the future is run. By assigning it to id first, we are actually copying the data, and it's the copy (id) that will be moved.
But this might not be what you want, if you're trying to get a more general solution where you can access the object inside the async function. In that case, it gets a bit tricker, and you might want to consider not using async functions if possible. If you must, it might be possible to have a separate object with the data you need which is surrounded by std::rc::Rc, which can then be moved into the async block without duplicating the underlying data.

What is the Rust equivalent of C++'s shared_from_this?

I have an object that I know that is inside an Arc because all the instances are always Arced. I would like to be able to pass a cloned Arc of myself in a function call. The thing I am calling will call me back later on other threads.
In C++, there is a standard mixin called enable_shared_from_this. It enables me to do exactly this
class Bus : public std::enable_shared_from_this<Bus>
{
....
void SetupDevice(Device device,...)
{
device->Attach(shared_from_this());
}
}
If this object is not under shared_ptr management (the closest C++ has to Arc) then this will fail at run time.
I cannot find an equivalent.
EDIT:
Here is an example of why its needed. I have a timerqueue library. It allows a client to request an arbitrary closure to be run at some point in the future. The code is run on a dedicated thread. To use it you must pass a closure of the function you want to be executed later.
use std::time::{Duration, Instant};
use timerqueue::*;
use parking_lot::Mutex;
use std::sync::{Arc,Weak};
use std::ops::{DerefMut};
// inline me keeper cos not on github
pub struct MeKeeper<T> {
them: Mutex<Weak<T>>,
}
impl<T> MeKeeper<T> {
pub fn new() -> Self {
Self {
them: Mutex::new(Weak::new()),
}
}
pub fn save(&self, arc: &Arc<T>) {
*self.them.lock().deref_mut() = Arc::downgrade(arc);
}
pub fn get(&self) -> Arc<T> {
match self.them.lock().upgrade() {
Some(arc) => return arc,
None => unreachable!(),
}
}
}
// -----------------------------------
struct Test {
data:String,
me: MeKeeper<Self>,
}
impl Test {
pub fn new() -> Arc<Test>{
let arc = Arc::new(Self {
me: MeKeeper::new(),
data: "Yo".to_string()
});
arc.me.save(&arc);
arc
}
fn task(&self) {
println!("{}", self.data);
}
// in real use case the TQ and a ton of other status data is passed in the new call for Test
// to keep things simple here the 'container' passes tq as an arg
pub fn do_stuff(&self, tq: &TimerQueue) {
// stuff includes a async task that must be done in 1 second
//.....
let me = self.me.get().clone();
tq.queue(
Box::new(move || me.task()),
"x".to_string(),
Instant::now() + Duration::from_millis(1000),
);
}
}
fn main() {
// in real case (PDP11 emulator) there is a Bus class owning tons of objects thats
// alive for the whole duration
let tq = Arc::new(TimerQueue::new());
let test = Test::new();
test.do_stuff(&*tq);
// just to keep everything alive while we wait
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
}
cargo toml
[package]
name = "tqclient"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
timerqueue = { git = "https://github.com/pm100/timerqueue.git" }
parking_lot = "0.11"
There is no way to go from a &self to the Arc that self is stored in. This is because:
Rust references have additional assumptions compared to C++ references that would make such a conversion undefined behavior.
Rust's implementation of Arc does not even expose the information necessary to determine whether self is stored in an Arc or not.
Luckily, there is an alternative approach. Instead of creating a &self to the value inside the Arc, and passing that to the method, pass the Arc directly to the method that needs to access it. You can do that like this:
use std::sync::Arc;
struct Shared {
field: String,
}
impl Shared {
fn print_field(self: Arc<Self>) {
let clone: Arc<Shared> = self.clone();
println!("{}", clone.field);
}
}
Then the print_field function can only be called on an Shared encapsulated in an Arc.
having found that I needed this three times in recent days I decided to stop trying to come up with other designs. Maybe poor data design as far as rust is concerned but I needed it.
Works by changing the new function of the types using it to return an Arc rather than a raw self. All my objects are arced anyway, before they were arced by the caller, now its forced.
mini util library called mekeeper
use parking_lot::Mutex;
use std::sync::{Arc,Weak};
use std::ops::{DerefMut};
pub struct MeKeeper<T> {
them: Mutex<Weak<T>>,
}
impl<T> MeKeeper<T> {
pub fn new() -> Self {
Self {
them: Mutex::new(Weak::new()),
}
}
pub fn save(&self, arc: &Arc<T>) {
*self.them.lock().deref_mut() = Arc::downgrade(arc);
}
pub fn get(&self) -> Arc<T> {
match self.them.lock().upgrade() {
Some(arc) => return arc,
None => unreachable!(),
}
}
}
to use it
pub struct Test {
me: MeKeeper<Self>,
foo:i8,
}
impl Test {
pub fn new() -> Arc<Self> {
let arc = Arc::new(Test {
me: MeKeeper::new(),
foo:42
});
arc.me.save(&arc);
arc
}
}
now when an instance of Test wants to call a function that requires it to pass in an Arc it does:
fn nargle(){
let me = me.get();
Ooddle::fertang(me,42);// fertang needs an Arc<T>
}
the weak use is what the shared_from_this does so as to prevent refcount deadlocks, I stole that idea.
The unreachable path is safe because the only place that can call MeKeeper::get is the instance of T (Test here) that owns it and that call can only happen if the T instance is alive. Hence no none return from weak::upgrade

How do I get a function pointer from a trait in Rust?

How do I get over something like this:
struct Test {
foo: Option<fn()>
}
impl Test {
fn new(&mut self) {
self.foo = Option::Some(self.a);
}
fn a(&self) { /* can use Test */ }
}
I get this error:
error: attempted to take value of method `a` on type `&mut Test`
--> src/main.rs:7:36
|
7 | self.foo = Option::Some(self.a);
| ^
|
= help: maybe a `()` to call it is missing? If not, try an anonymous function
How do I pass a function pointer from a trait? Similar to what would happen in this case:
impl Test {
fn new(&mut self) {
self.foo = Option::Some(a);
}
}
fn a() { /* can't use Test */ }
What you're trying to do here is get a function pointer from a (to use Python terminology here, since Rust doesn't have a word for this) bound method. You can't.
Firstly, because Rust doesn't have a concept of "bound" methods; that is, you can't refer to a method with the invocant (the thing on the left of the .) already bound in place. If you want to construct a callable which approximates this, you'd use a closure; i.e. || self.a().
However, this still wouldn't work because closures aren't function pointers. There is no "base type" for callable things like in some other languages. Function pointers are a single, specific kind of callable; closures are completely different. Instead, there are traits which (when implemented) make a type callable. They are Fn, FnMut, and FnOnce. Because they are traits, you can't use them as types, and must instead use them from behind some layer of indirection, such as Box<FnOnce()> or &mut FnMut(i32) -> String.
Now, you could change Test to store an Option<Box<Fn()>> instead, but that still wouldn't help. That's because of the other, other problem: you're trying to store a reference to the struct inside of itself. This is not going to work well. If you manage to do this, you effectively render the Test value permanently unusable. More likely is that the compiler just won't let you get that far.
Aside: you can do it, but not without resorting to reference counting and dynamic borrow checking, which is out of scope here.
So the answer to your question as-asked is: you don't.
Let's change the question: instead of trying to crowbar a self-referential closure in, we can instead store a callable that doesn't attempt to capture the invocant at all.
struct Test {
foo: Option<Box<Fn(&Test)>>,
}
impl Test {
fn new() -> Test {
Test {
foo: Option::Some(Box::new(Self::a)),
}
}
fn a(&self) { /* can use Test */ }
fn invoke(&self) {
if let Some(f) = self.foo.as_ref() {
f(self);
}
}
}
fn main() {
let t = Test::new();
t.invoke();
}
The callable being stored is now a function that takes the invocant explicitly, side-stepping the issues with cyclic references. We can use this to store Test::a directly, by referring to it as a free function. Also note that because Test is the implementation type, I can also refer to it as Self.
Aside: I've also corrected your Test::new function. Rust doesn't have constructors, just functions that return values like any other.
If you're confident you will never want to store a closure in foo, you can replace Box<Fn(&Test)> with fn(&Test) instead. This limits you to function pointers, but avoids the extra allocation.
If you haven't already, I strongly urge you to read the Rust Book.
There are few mistakes with your code. new function (by the convention) should not take self reference, since it is expected to create Self type.
But the real issue is, Test::foo expecting a function type fn(), but Test::a's type is fn(&Test) == fn a(&self) if you change the type of foo to fn(&Test) it will work. Also you need to use function name with the trait name instead of self. Instead of assigning to self.a you should assign Test::a.
Here is the working version:
extern crate chrono;
struct Test {
foo: Option<fn(&Test)>
}
impl Test {
fn new() -> Test {
Test {
foo: Some(Test::a)
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
test.foo.unwrap()(&test);
}
Also if you gonna assign a field in new() function, and the value must always set, then there is no need to use Option instead it can be like that:
extern crate chrono;
struct Test {
foo: fn(&Test)
}
impl Test {
fn new() -> Test {
Test {
foo: Test::a
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
(test.foo)(&test); // Make sure the paranthesis are there
}

Copying a struct for use on another thread

I have a struct:
struct MyData {
x: i32
}
I want to asynchronously start a long operation on this struct.
My first attempt was this:
fn foo(&self) { //should return immediately
std::thread::Thread::spawn(move || {
println!("{:?}",self.x); //consider a very long operation
});
}
Clearly the compiler cannot infer an appropriate lifetime due to conflicting requirements because self may be on the stack frame and thus cannot be guaranteed to exist by the time the operation is running on a different stack frame.
To solve this, I attempted to make a copy of self and provide that copy to the new thread:
fn foo(&self) { //should return immediately
let clone = self.clone();
std::thread::Thread::spawn(move || {
println!("{:?}",clone.x); //consider a very long operation
});
}
I think that does not compile because now clone is on the stack frame which is similar to before. I also tried to do the clone inside the thread, and that does not compile either, I think for similar reasons.
Then I decided maybe I could use a channel to push the copied data into the thread, on the theory that perhaps channel can magically move (copy?) stack-allocated data between threads, which is suggested by this example in the documentation. However the compiler cannot infer a lifetime for this either:
fn foo(&self) { //should return immediately
let (tx, rx) = std::sync::mpsc::channel();
tx.send(self.clone());
std::thread::Thread::spawn(move || {
println!("{:?}",rx.recv().unwrap().x); //consider a very long operation
});
}
Finally, I decided to just copy my struct onto the heap explicitly, and pass an Arc into the thread. But not even here can the compiler figure out a lifetime:
fn foo(&self) { //should return immediately
let arc = std::sync::Arc::new(self.clone());
std::thread::Thread::spawn(move || {
println!("{:?}",arc.clone().x); //consider a very long operation
});
}
Okay borrow checker, I give up. How do I get a copy of self onto my new thread?
I think your issue is simply because your structure does not derive the Clone trait. You can get your second example to compile and run by adding a #[derive(Clone)] before your struct's definition.
What I don't understand in the compiler behaviour here is what .clone() function it tried to use here. Your structure indeed did not implement the Clone trait so should not by default have a .clone() function.
playpen
You may also want to consider in your function taking self by value, and let your caller decide whether it should make a clone, or just a move.
As an alternative solution, you could use thread::scoped and maintain a handle to the thread. This allows the thread to hold a reference, without the need to copy it in:
#![feature(old_io,std_misc)]
use std::thread::{self,JoinGuard};
use std::old_io::timer;
use std::time::duration::Duration;
struct MyData {
x: i32,
}
// returns immediately
impl MyData {
fn foo(&self) -> JoinGuard<()> {
thread::scoped(move || {
timer::sleep(Duration::milliseconds(300));
println!("{:?}", self.x); //consider a very long operation
timer::sleep(Duration::milliseconds(300));
})
}
}
fn main() {
let d = MyData { x: 42 };
let _thread = d.foo();
println!("I'm so fast!");
}

Resources