-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Downcast a JsValue to an exported struct #2231
Comments
Ah yeah currently this isn't implemented. It would require an intrinsic of one form or another since the JS class lives in the JS file to test inheritance. This would be a nice feature to have though! |
We've developed a workaround that discovers the runtime class name through
Currently, wasm-bindgen/crates/backend/src/codegen.rs Line 139 in 1795020
js_name that could be added. Would adding another trait to wasm_bindgen::convert that exposes this metadata and generating it from the derive macro be the preferred way to do this?
Also, should the method that performs the downcast be added to |
Thanks for this workaround. It works, however, I get this error message: I don't know why. Any ideas? |
This might be relevant #1578 (comment). |
Thanks, the situation was a bit different. After checking the source code, the error meant 3 possible things:
However, none of the above was true in my case. In my case, I dispatched a custom event, and there were two listeners for that event. Thus the |
Got the same error. However, I neither have callbacks nor multiple ownership in my code. I already spent a few hours trying to fix this error, but I am simply stuck. Does anyone have an idea, where this error might come from?
Here is the relevant code: Calling the generic_of_jsval function: // ignore getter as defined in another struct:
#[wasm_bindgen(getter)]
pub fn ignore(&self) -> RangeArray {
self.ignore.clone()
}
// calling the from function, which then calls generic_of_jsval (see below):
pub fn ignore_vec (&self) -> Vec<Range> { Vec::from(self.ignore()) } Definitions: #[wasm_bindgen]
#[derive(Clone)]
pub struct Range {
start: js_sys::Number,
end: js_sys::Number,
}
#[wasm_bindgen]
impl Range {
#[wasm_bindgen(constructor)]
pub fn new(start: js_sys::Number, end: js_sys::Number) -> Range {
Range {
start,
end,
}
}
#[wasm_bindgen(getter)]
pub fn start(&self) -> js_sys::Number {
self.start.clone()
}
#[wasm_bindgen(getter)]
pub fn end(&self) -> js_sys::Number {
self.end.clone()
}
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "Array<Range>")]
#[derive(Clone, Debug)]
pub type RangeArray;
}
impl From<RangeArray> for Vec<Range> {
fn from(range_array: RangeArray) -> Self {
let arr: Result<Array, RangeArray> = range_array.dyn_into::<Array>();
arr.map_or(vec![], |array: Array| {
array.iter()
.filter_map(|js_val_range: JsValue|
generic_of_jsval(js_val_range, "Range").ok())
.collect()
})
}
} |
Thank you for workaround! You saved me a lot of time )
Note that return type is now not T itself, but T::Anchor, that is defined as @fxdave @AlexW00 as I tested, it solves issue with
|
@AlexKorn One problem with your solution I just ran into is that when I build my code for production, the minimizer changes the name of my struct to So, I'm currently trying to find a way to get the current JavaScript class name for my Rust struct. |
Got the same problem ) UPD: something less hacky: add wasm-exported method to each struct that just returns its classname, ex. |
For now, I just went the easy way of marking the function |
I wrapped the solution in dirty proc macro:
Cargo.toml:
It returns copy of the value, not the reference, and relies on static method Usage example:
|
Does You can do:
|
|
I worked around by using helper JS functions and bindings, which lets wasm-bindgen handle all that unsafe stuff: // /js/cast.js
export function castInto(value) {
return value;
}
export function castRef(value, callback) {
callback(value);
} #[wasm_bindgen(module = "/js/cast.js"))]
extern "C" {
// This will let Rust regain ownership of `Foo`
#[wasm_bindgen(js_name = castInto)]
pub fn cast_into_foo(value: JsValue) -> Foo;
// This will let you get a reference to `Foo` in the `callback` closure (JS retains ownership of `Foo`)
#[wasm_bindgen(js_name = castRef)]
pub fn cast_ref_foo(value: &JsValue, callback: &mut dyn FnMut(&Foo));
} This can be improved by adding type checks in the JS functions and returning |
@AlexKorn thank you for that workaround with the proc macro! Would it be okay with you if I make it into a crate (referencing you as the author, of course)? Or perhaps you've already done so? |
No, I have not, feel free to use it as you wish, taking in consideration that the solution itself belongs to aweinstock314 =) |
Published as https://crates.io/crates/wasm-bindgen-derive with minor changes (decided to go for a more general name, in case there are other derive macros to be added). I added the attribution to @AlexKorn and @aweinstock314 in https://docs.rs/wasm-bindgen-derive/latest/wasm_bindgen_derive/derive.TryFromJsValue.html, please tell me if you would prefer it to be more prominent. |
We've unfortunately regressed on this as a part of #3709. |
Summary
In the code below, how can I cast the
JsValue
toFoo
?Additional Details
From<Foo> for JsValue
is implemented automatically, so I'd expect something likeTryInto<Foo> for JsValue
to be implemented too, but I couldn't find anything.Reasons why you'd want to do this
Vec<Foo>
and&[Foo]
are unsupported, butVec<JsValue>
and&[JsValue]
are supported, I think)The text was updated successfully, but these errors were encountered: