#How to call JS function with 4 parameters? (wasm-bindgen)
1 messages · Page 1 of 1 (latest)
you can use Function::apply() to call with any number of arguments https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Function.html#method.apply
well, that involves creating a new JsValue Array, which would be detrimental on performance, to create a new array object for every function call -- the reason i'm using rust to write a web thing, is to write things that are high performance
i was told that i need to "define my own call4" -- but for the life of me, i can't figure out how to do that in rust
any way i try, i get the error
cannot define inherent 'impl' for a type outside of the crate where the type is defined
the way i was advised, this seems like this is straightforward in rust, i just can't figure out the right syntax to "define my own method" for this, to extend js_sys in this way
@cosmic root
i was looking in js_sys source code:
#[wasm_bindgen(method, catch, js_name = call)]
pub fn call3(
this: &Function,
context: &JsValue,
arg1: &JsValue,
arg2: &JsValue,
arg3: &JsValue,
) -> Result<JsValue, JsValue>;
☝️ this is how call3 is provided -- i can't figure out how to simply add my own call4 definition by following the above pattern.
🤔 i could imagine forking js_sys and literally adding call4 through to call99 -- but surely that's ridiculous, surely this library is assuming that people familiar with rust know a simple way to "define their own" calling methods -- but for the life of me, i can't figure out the right pattern that doesn't cause the error i mentioned above?
have you tried just pasting that into your own code in an extern block and seeing what happens?
that seems like it might generate the right code
yes, it gave me the above error -- i'll post some example of what i've tried
not in an impl
it's just a function
in order to make it an impl you would need to define an extension trait, which is a complication so don't bother with that for now
i've tried this:
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(method, catch, js_name = call)]
pub fn call4(
this: &js_sys::Function,
context: &JsValue,
arg1: &JsValue,
arg2: &JsValue,
arg3: &JsValue,
arg4: &JsValue,
) -> Result<JsValue, JsValue>;
}
but i get error
error[E0116]: cannot define inherent 'impl' for a type outside of the crate where the type is defined
ah, I see, the method option generates such an impl block, which is why it's failing https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/method.html
I don't know how you get a wasm-bindgen method to pass a JS this of your choice, though
(I am not at all an expert here; there may be a way)
now, straight from the js_sys source code in lib.rs:
// Function
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object, is_type_of = JsValue::is_function, typescript_type = "Function")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub type Function;
// ... more functions ...
#[wasm_bindgen(method, catch, js_name = call)]
pub fn call3(
this: &Function,
context: &JsValue,
arg1: &JsValue,
arg2: &JsValue,
arg3: &JsValue,
) -> Result<JsValue, JsValue>;
}
☝️ seeing the context around call3 might help understand the situation enough to "define your own call4" ?
i think i've figured out how to pass in a JsValue::null() as a javascript this, which is called context in the above call3.
hmm
right but that's the this for the function being called, that's not the function being called, which is the this of the JS method Function.prototype.call
fascinating, that's getting slightly meta
that's the nature of .call!
so, if i'm getting this right, #[wasm_bindgen] generates a function implementation.
i need to create call4.
but if i declare this on extern "C", i get the error that i can't mess around with implementations that belong to other crates.
that's not because of extern
that's because of #[wasm_bindgen(method)] _in particular
https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/method.html
This [example] generates a
hasmethod onSetin Rust
that's the problem
the macro is generating an impl Function {} block for you
which doesn't work if you didn't declare that type
this seems like a gap in wasm-bindgen's capabilities, to me, and I think whoever told you "define your own call4" either didn't know this, or was thinking you would write a JS function that forwards
you can still do that, if you want
write a little JS function that forwards 4 args, then write a wasm-bindgen declaration for that
oh no, that sounds terrible.
so you're saying, it might be an undersight of wasm-bindgen's design -- it's not designed to be capable to pass more than 3 parameters?
is this something that wasm-bindgen needs a PR to fix?
i mean, it seems obvious to me that it's desirable to call functions and pass N arguments
you can embed a bit of JS with https://rustwasm.github.io/wasm-bindgen/reference/js-snippets.html
right, but that technique only allows me to import module-level functions -- that's not suitable for interacting with javascript at runtime
no, I'm saying that it's an oversight that they didn't provide a way to call a JS method while declaring a Rust non-method function
I mean they show
#[wasm_bindgen(inline_js = "export function add(a, b) { return a + b; }")]
extern "C" {
fn add(a: u32, b: u32) -> u32;
}
so do this but with
export function call4(f, arg1, arg2, arg3, arg4) { return f(arg1, arg2, arg3, arg4); }
okay, so the solution is to create my own js module, with functions for calling functions?
fascinating
that's what wasm-bindgen is doing anyway, in a sense; it has to generate lots of JS
this is just you providing some custom JS to go with the generated JS
very interesting -- if i'm understanding you correctly, it does seem astonishing to me, that what feels like weird custom hacking, is required for basically any level of reasonable interop with javascript -- i just can hardly believe that this is what people are expected to do -- it makes me feel like i'm missing a much simpler straightforward way -- it hardly seems esoteric to simply want to call javascript functions in a straightforward manner... 🤔
@cosmic root i will now implement these helper functions and let you know how it goes
I think you're overestimating the average quality of libraries a bit!
hahahah maybe so 😄
there are often things that are, in fact, just missing
and to get added, someone has to write the addition
and this doesn't happen until someone has a need for them
amazing to think i'm one of the first to need to call javascript functions that might have more than 4 arguments 😄
notice that this generally only comes up with callbacks
lots of functions have 4 or more parameters but they belong to some namespace or class, and so they're called with generated wrappers for that function specifically, not via Function
hmm fascinating
i started by trying to learn how to pass in the wasm importsObject, but apparently that's not possible -- i'm just trying to find a solid way for the rust code to call javascript functions -- but javascript functions at runtime because module-level functions are not useful to an ongoing javascript program...
that's how i've landed on making callbacks work, because wasm-bindgen doesn't give access to providing functions at runtime -- it's been quite a rabbit hole to say the least...
@cosmic root okay, here's what i've got:
➡️ src/callers.js
export function call4(func, context, arg1, arg2, arg3, arg4) {
return func.call(context, arg1, arg2, arg3, arg4)
}
export function call5(func, context, arg1, arg2, arg3, arg4, arg5) {
return func.call(context, arg1, arg2, arg3, arg4, arg5)
}
➡️ src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "/src/callers.js")]
extern "C" {
fn call4(
this: &js_sys::Function,
context: &JsValue,
arg1: &JsValue,
arg2: &JsValue,
arg3: &JsValue,
arg4: &JsValue,
) -> Result<JsValue, JsValue>;
}
❌ error[E0277]: the trait bound "Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>: FromWasmAbi" is not satisfied
huff...
i just want to declare in my rust code
"i expect these javascript functions to be provided at runtime"
and then be able to call those.
what you said earlier has me thinking, there surely must be a straightforward approach to do that.
I think you need the catch attr
that goes along with having the return type being -> Result
hmm, okay i added
#[wasm_bindgen(method, catch, js_name = call4)]
fn call4(
//...
but then suddenly the top of the extern block gives me this old error:
#[wasm_bindgen(module = "/src/callers.js")]
extern "C" {
//...
error[E0116]: cannot define inherent "impl" for a type outside of the crate where the type is defined
you need catch but not method
hey, no errors!
holy moly maybe this is it!
@cosmic root we did it 🎉 🎉 🎉
four parameters passed into javascript!
#[wasm_bindgen]
pub fn greet(name: &str, callback: &js_sys::Function) {
let greeting = &format!("Hello, {}!", name);
let this = JsValue::null();
let greeting_js = JsValue::from(greeting);
let argument2 = JsValue::from("argument2");
let argument3 = JsValue::from("argument3");
let argument4 = JsValue::from("argument4");
let _ = call4(
&callback,
&this,
&greeting_js,
&argument2,
&argument3,
&argument4,
);
}
Hello, Chase! argument2 argument3 argument4
it looks horrible, and hacky, and makes me a little sad -- but it works!
thanks so much @cosmic root, i hugely appreciate your help, it would have taken geological time for me to figure that out on my own
if the types are always those then you can remove some boilerplate by declaring them
that is, declare call4's signature to accept four &strs instead of JsValues
right, interesting
of course that's making the function more single-purpose, not a generic function-caller
but that's an option you have because you're writing this function
well, my goal is to chart out the "public javascript api" of functions that rust can call
and you get the static typing benefits
from javascript, i want to
- "initialize the rust-wasm module"
- "provide rust with the following javascript functions, for rust to call"
- "call rust-defined functions"
it's been very painful reverse-engineering to build a very simple and straightforward bridge between rust's wasm output, and javascript.
i still have the sneaking suspicion that i'm misunderstanding a simpler way here, but i've read the documentation upside-down and sideways, and haven't figured a better path.
i've seen a lot of unanswered confusion, similar to mine, that it seems wasm-bindgen isn't really designed to interoperate with javascript as much as "replace" javascript -- wasm was designed to ingest a simple "imports" object containing javascript functions that become callable -- but wasm-bindgen offers no access to it 😦
once i learn rust more fully, it makes me feel compelled to perhaps one day fix this problem, and write a replacement library that handles this in a much simpler and straightforward way.
thanks again for your help.