You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, I'm currently having a problem when passing a struct from Rust to JavaScript, as I want to keep/receive modifications made by a JavaScript callback in Rust. I also asked this on Stack Overflow.
I'll include the code snippets here, but for a full example you can get run quickly check out this Git repo.
As far as I am aware it's not possible to use serde to pass data in this case, because I want to call functions on the JavaScript object. Or did I miss something here?
Additional Details
Basically what I have is the following struct (see lib.rs in example):
#[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 structpubstructSomeStruct{pub(crate)field_to_be_modified:i32,}#[wasm_bindgen]implSomeStruct{#[wasm_bindgen(constructor)]pubfnnew() -> Self{set_panic_hook();Self{field_to_be_modified:0,}}pubfnmodify_field(&mutself,value:i32){self.field_to_be_modified = value;}pubfnfield(&self) -> i32{self.field_to_be_modified}#[wasm_bindgen]pubasyncfnwith_callback(&self,function_or_promise:JsValue) -> Result<JsValue,JsValue>{letmut 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 runs either a promise or a functionrun_any_function(&mut s, function,vec![JsValue::from(1u32)]).await}}
Open rest of the code & how the JS function is called
pub(crate)asyncfnrun_any_function(ax:&mutSomeStruct,function_or_promise: js_sys::Function,arguments:Vec<JsValue>,) -> Result<JsValue,JsValue>{let result = run_function(ax, function_or_promise, arguments)?;// Handle functions defined like "async function(args) {}"if result.has_type::<js_sys::Promise>(){returnrun_promise(result).await;}else{Ok(result)}}asyncfnrun_promise(promise_arg:JsValue) -> Result<JsValue,JsValue>{let promise = js_sys::Promise::from(promise_arg);let future = JsFuture::from(promise);
future.await}fnrun_function(my_struct:&mutSomeStruct,function: js_sys::Function,arguments:Vec<JsValue>,) -> Result<JsValue,JsValue>{let args = Array::new();// This is the reason modifications from JS aren't reflected in Rust, but without it JsValue::from doesn't worklet 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 functionlet 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;Ok(result)}
And I want to use it from JS like the following (see index.html in example):
import*asmvefrom'./pkg/mve.js';asyncfunctionrun(){letmodule=awaitmve.default();lets=newmve.SomeStruct();console.log("Initial value (should be 0):",s.field());awaits.with_callback(function(s_instance,second_arg,third_arg){// s_instance is of type SomeStruct, and is a COPY of sconsole.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 copys_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_instance// TODO: How to make this work?console.log("Field value after callback (should be 42):",s.field());}run();
Another problem I ran into is when using s directly in the callback (this would circumvent my problem sufficiently, however it doesn't work): Error: recursive use of an object detected which would lead to unsafe aliasing in rust. So what I'm trying to do seems impossible, but I think it shouldn't be?
The problem is that I can't figure out how to pass a mutable reference of that struct (as a JsValue) to the JavaScript function. This is why in run_function a clone of the struct is passed, but obviously this doesn't keep the modifications the JS Code does to that struct.
Is there a way to pass a reference of the struct (as JsValue) to the JS function directly, without cloning? Or is there another way to keep modifications? I would also be happy about a way to pass a clone, and then copy the modifications to the original struct (but this didn't work because of borrow checker errors -- after all, I can't seem to access clone after it was passed to JsValue::from.
Thank you in advance!
The text was updated successfully, but these errors were encountered:
Summary
Hi, I'm currently having a problem when passing a struct from Rust to JavaScript, as I want to keep/receive modifications made by a JavaScript callback in Rust. I also asked this on Stack Overflow.
I'll include the code snippets here, but for a full example you can get run quickly check out this Git repo.
As far as I am aware it's not possible to use serde to pass data in this case, because I want to call functions on the JavaScript object. Or did I miss something here?
Additional Details
Basically what I have is the following struct (see
lib.rs
in example):Open rest of the code & how the JS function is called
And I want to use it from JS like the following (see
index.html
in example):Another problem I ran into is when using
s
directly in the callback (this would circumvent my problem sufficiently, however it doesn't work):Error: recursive use of an object detected which would lead to unsafe aliasing in rust
. So what I'm trying to do seems impossible, but I think it shouldn't be?The problem is that I can't figure out how to pass a mutable reference of that struct (as a JsValue) to the JavaScript function. This is why in
run_function
a clone of the struct is passed, but obviously this doesn't keep the modifications the JS Code does to that struct.Is there a way to pass a reference of the struct (as JsValue) to the JS function directly, without cloning? Or is there another way to keep modifications? I would also be happy about a way to pass a clone, and then copy the modifications to the original struct (but this didn't work because of borrow checker errors -- after all, I can't seem to access
clone
after it was passed toJsValue::from
.Thank you in advance!
The text was updated successfully, but these errors were encountered: