wasm-bindgen: Allow returning Vec
It would really be great to be able to return a vector of structs or tuples:
For example. I have the following type:
#[wasm_bindgen]
struct Range {
offset: u32,
length: u32
}
that I want to return in my function
#[wasm_bindgen]
pub fn token_ranges(text: &str) -> Vec<Range>
I am getting this error:
Compiling picl_wasm_runtime v0.1.0 (file:///A:/Repos/picl_native_runtime/bindings/typescript)
error[E0277]: the trait bound `std::boxed::Box<[Range]>: wasm_bindgen::convert::WasmBoundary` is not satisfied
--> src\lib.rs:15:1
|
15 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ the trait `wasm_bindgen::convert::WasmBoundary` is not implemented for `std::boxed::Box<[Range]>`
|
= help: the following implementations were found:
<std::boxed::Box<[u16]> as wasm_bindgen::convert::WasmBoundary>
<std::boxed::Box<[i16]> as wasm_bindgen::convert::WasmBoundary>
<std::boxed::Box<[f32]> as wasm_bindgen::convert::WasmBoundary>
<std::boxed::Box<[i32]> as wasm_bindgen::convert::WasmBoundary>
and 5 others
= note: required because of the requirements on the impl of `wasm_bindgen::convert::WasmBoundary` for `std::vec::Vec<Range>`
My workaround is to flatten my data and return a Vec<u32> and then splice my token ranges on the JS side. This is unfortunate…
How can I add a WasmBoundary trait for a custom type? Is there a better way?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 128
- Comments: 60 (15 by maintainers)
Links to this issue
Commits related to this issue
- feat(wasm) Export `Vec<Block>` to wasm. So, `wasm-bindgen` does not support `Vec<T>` (see https://github.com/rustwasm/wasm-bindgen/issues/111), so my quick and dirty solution right now is to serializ... — committed to Hywan/gutenberg-parser-rs by Hywan 6 years ago
- Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... uncon... — committed to rustwasm/wasm-bindgen by alexcrichton 6 years ago
- Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... uncon... — committed to rustwasm/wasm-bindgen by alexcrichton 6 years ago
- Compiled wasm interface. https://github.com/rustwasm/wasm-bindgen/issues/111 — committed to alvarosan/saturno by alvarosan 5 years ago
- Add TypeScript return type for my_bad_words https://github.com/rustwasm/wasm-bindgen/issues/111#issuecomment-625729949 — committed to Kinrany/likelike-online-rs by Kinrany 4 years ago
- Implement NatureFilter::from_natures Due to wasm-bindgen's limitations [1], we need a constructor that takes a `Vec<u32>` since we cannot take a `Vec<Nature>`. As a result, in Rust-land we go through... — committed to sliminality/raidtomi by sliminality 3 years ago
- feat: resize images to 2 sizes and upload them to R2 refactor: lots of refactoring to backend and client WIP: ``` // TODO read https://github.com/rustwasm/wasm-bindgen/issues/111 and use some workaro... — committed to ben-xD/bounding_box_annotation_flutter by ben-xD 2 years ago
This issue is kind of a blocker for pretty much any sort of a bigger project using rustwasm.
For those looking for a workaround on this, if you can turn your data into a
Vec<u8>or&[u8]A good example of how to use this is creating a texture in Rust to render in Javascript, so for example:
It would still be awesome to have builtin support for something like this which is quite normal case:
@alexcrichton Exactly, that’s precisely my usecase.
It’s not a complete solution, but I created #1749 which adds in
FromIteratorforArray:This means that now you can send
Vec<T>to JS, you just have to return anArrayand use.into_iter().collect()to convert theVec<T>into anArray.I’m with a similar issue here, but with a complex struct.
code:
error:
@rookboom @Hywan do y’all basically need
Vec<T>whereThas#[wasm_bindgen]on it?@dragly As explained in the PR, you need to use
.map(JsValue::from), like this:This is because structs are a Rust data type, and so you have to manually use
JsValue::fromto convert them into a JS data type (the same is true for other Rust data types like&str,i32, etc.).If anyone is still looking at this, I was able to work around this using Serde to serialize/deserialize the data. This was the guide I used: https://rustwasm.github.io/docs/wasm-bindgen/reference/arbitrary-data-with-serde.html
Edit: For those wanting to avoid JSON serialization, the guide above also includes a link to serde-wasm-bindgen which “leverages direct APIs for JavaScript value manipulation instead of passing data in a JSON format.”
My workaround is to pass JSON over the wasm boundary… Not ideal but works for now.
Hi guys, great to see a PR has been merged. Is there any ETA for this feature ? Or is it possible for you to trigger a new release, so we can have this feature using cargo add.
Thanks
The docs seem to incorrectly indicate that this is already supported: https://rustwasm.github.io/wasm-bindgen/reference/types/boxed-jsvalue-slice.html
@zimond Yes, sorry my bad.
@Pauan @alexcrichton Do you guys have a simple example which we could use as a guide to implement a function that returns
Vec<Struct>?@Kinrany Yes, but it requires a bit of a hack:
(The
typescript_typeattribute can specify any TypeScript type, even complex types like|)Now you just do
fn foo() -> MyArrayand use.unchecked_into::<MyArray>()to cast theArrayintoMyArray.Great work @Liamolucko! I look forward to using this once released.
@bushrat011899
I have had no response from @chinedufn @alexcrichton regarding the failing test. I spent a lot of time on this PR and this is discouraging to say the least. I wonder if they just never look at PRs unless they pass all tests. In which case, I could assume an interpretation of the comment in the test, and get it to pass. That way I’d get their attention, and if I assumed the wrong interpretation they will tell me to change it. However the PR is quite old now and probably has merge conflicts with master to sort out. If I’m going to do more work on this I want some assurance that I’m not wasting my time.
That really is unfortunate, this seems like a really obvious feature to work on in my opinion. My first thought for something useful with WASM was to write a REST API client. That way, I could have type guarantees and unit tests that, for example, my Axum server was interacting with a web UI correctly. Kinda hard to do that if something simple like “give me a list of data” requires type erasure.
I was able to get this solution working for me, as a disclaimer, I just hacked it together and it could be greatly improved but I wanted to get something working before i attempted to clean it up into macros.
Cargo.tomllib.rsI believe latest wasm-bindgen release already allows return
Vec<T>whereTis any type implementingWasmDescribe + JsCast. Currently available types includes primitives, and types fromjs-sys. So returningVec<JsValue>is fine now.Edit:
#[wasm_bindgen]do not generate the trait impls. So there’s more work to be doneUnfortunately, solutions with
js_syshave huge performance overheads. You can also returnBox<[T]>withTbeing basic numeric type which is efficient on the JS side (it just slices relevant wasm memory). This however needs a copy on the Rust side. In general, it is faster thanjs_systhough.What I come up with (and seems to be faster by a margin) is to have the following wrapper type:
This is both zero-copy on the rust side and single copy between wasm memory and JS on the JS side. Basically, the type just emulates ref to a slice through standard wasm bindgen API (which means support for returning
&[T]should be relatively easy to add if somebody knows internals of bindgen) A caveat is that you shouldn’t save MySlice, it should only be used to return immediate data to JS (because it detaches pointers, the code cannot guarantee lifetimes)Usage is simple:
with the JS side using
@9oelM That’s because you explicitly declared that
decoded_bytesis&Vec<u8>. There’s another APIVec::as_slicewhich could give you a&[u8]fromVec<u8>. So you could usedecoded_bytes.as_slice().into(), orUint8Array::from(decoded_bytes.as_slice()).More learning materials: Deref coercions, Type conversion , Lifetime ellision (which is why
&'a [u8]and&[u8]are the same)Rather than returning pointers and lengths manually, you can use this, which should be slightly less error prone: https://docs.rs/js-sys/0.3.9/js_sys/struct.Uint8Array.html#method.view
Gabriel and rook - have y’all found a workaround? Solving or working around this would add much flexibility to WASM in Rust.
Eventually, being able to use HashMaps, or structs from other packages (Like ndarrays) would be nice, but having some type of collection that maps to JS arrays would be wonderful; not sure if I can continue my project without this.
@9oelM there is
From<&[u8]>implemented for Uint8Array, so basically you could just returndecoded_bytes.into()@i-schuetz : the workaround that I use is having a return type of
Vec<JsValue>and converting your vector to it on return:myvec.iter().map(JsValue::from).collect().