-
-
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
Allow returning Vec<T> #111
Comments
Thanks for the report! Right now there's not a great way to do this in wasm-bindgen itself in terms of the conversion here has to be also accompanied with a conversion on the JS side which may be somewhat tricky. It should certainly be possible though given enough support! |
Same issue here :-). What kind of support do you need? |
So, `wasm-bindgen` does not support `Vec<T>` (see rustwasm/wasm-bindgen#111), so my quick and dirty solution right now is to serialize the vector to JSON, and parse it from the JS land.
@alexcrichton Exactly, that's precisely my usecase. |
Yes, that would be fantastic. |
Ok this is then I think pretty similar to #104. We somehow need a way to communicate this to the CLI tool but currently the strategy for doing that is quite limited. |
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... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
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... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
I'm with a similar issue here, but with a complex struct. code:
error:
|
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. |
My workaround is to pass JSON over the wasm boundary... Not ideal but works for now. |
For those looking for a workaround on this, if you can turn your data into a #[wasm_bindgen]
pub struct ByteStream {
offset: *const u8,
size: usize,
}
#[wasm_bindgen]
impl ByteStream {
pub fn new(bytes: &[u8]) -> ByteStream {
ByteStream {
offset: bytes.as_ptr(),
size: bytes.len(),
}
}
pub fn offset(&self) -> *const u8 {
self.offset
}
pub fn size(&self) -> usize {
self.size
}
} A good example of how to use this is creating a texture in Rust to render in Javascript, so for example: #[wasm_bindgen]
pub fn render() -> ByteStream {
let texture = Vec::new();
// ...
ByteStream::new(&texture);
} const texture = render();
const textureRaw = new Uint8ClampedArray(memory.buffer, texture.offset(), texture.size());
const image = new ImageData(textureRaw, width, height); |
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 |
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." |
I'm wanting to take this one step further and return Vec<js_sys::Function> which means no serde serialization for me (Maybe there is some kind of ref I could serialize if I dug into the internals?). The workaround I'm kicking about at the moment is to create the collection on the JS side and expose some methods for appending and cleaning up. Something like...
Not suitable for prod work and by no means ideal or ergonomic but I'm really just thrashing around to see how far I can take |
This issue is kind of a blocker for pretty much any sort of a bigger project using rustwasm. |
It's not a complete solution, but I created #1749 which adds in use js_sys::Array;
#[wasm_bindgen]
pub fn token_ranges(text: &str) -> Array {
get_vec_somehow().into_iter().collect()
} This means that now you can send |
Same issue here :-). #[wasm_bindgen]
#[derive(Debug)]
pub struct HeartBeat {
pub template: u8,
pub classify: u8,
pub index: u32,
pub tr: u16,
pub hr: u16,
pub feature: [[f32; 20]; 3],
}
|
@Pauan Great! Thanks for looking into this. However, I am encountering the following error now.
from the following code: #[wasm_bindgen]
struct MyObject {
a: f32,
b: f32,
}
#[wasm_bindgen]
pub fn test() -> Array {
let objects = vec![
MyObject {
a: 123.0,
b: 1024.1,
},
MyObject {
a: 456.7,
b: 1024.8,
},
];
objects.into_iter().collect()
} Am I missing something? This is using wasm-bindgen = "0.2.51". Maybe your change is not in yet? The error is different from before, however, so it seems like something changed. |
@dragly As explained in the PR, you need to use objects.into_iter().map(JsValue::from).collect() This is because structs are a Rust data type, and so you have to manually use |
As a workaround, is it possible to also specify the TypeScript type returned by the function? By default |
@Kinrany Yes, but it requires a bit of a hack: #[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "Array<string>")]
type MyArray;
} (The Now you just do |
refactor: lots of refactoring to backend and client WIP: ``` // TODO read rustwasm/wasm-bindgen#111 and use some workaround to send ImagesBytes to server. ```
Is there any work on this issue still? I saw #2734 had a solution to this issue but needed additional help with passing tests. If a full resolution cannot be implemented without a breaking change, could the /// Current Workaround
#[wasm_bindgen]
pub fn manual_serialization() -> JsValue {
serde_wasm_bindgen::to_value::<Vec<String>>(&vec![]).unwrap()
}
/// Proposed Alternative
#[wasm_bindgen(serialize_output)]
pub fn automatic_serialization() -> Vec<String> {
vec![]
} This appears to be the workaround I've seen the most commonly used, but has a massive flaw in that all type information is lost for the developer and |
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. |
Because this issue still doesn't have a good workaround IMO, I've tried to make my own. Below is a single-file procedural attribute macro which modifies a function definition in-place to:
This doesn't fix the issue entirely. For example, I haven't published a crate before, and this is small enough that I don't know if it really needs to be, but this is how I've worked around this pretty massive functionality gap in Proc-Macro Crate
|
@bushrat011899: I hit this issue (and similar ones with argument types) before, and tried to generalize it as https://docs.rs/wasm-bindgen-derive/latest/wasm_bindgen_derive/ . Would be interesting to include your macro there as well. |
@fjarri I'm not surprised, it seems like a pretty easy issue to hit! Feel free to take the code I wrote and include it if you like. It's straightforward enough that I'm sure any reasonable person would end up converging on this independently anyway. |
Great work @Liamolucko! I look forward to using this once released. |
Hi guys, great to see a PR has been merged. Thanks |
See #3530. |
@daxpedda I checked that issue, still not released right ? |
No. |
When's the next release? The last one was in June. |
We don't have a timetable for a next release. |
Yes it has been. |
It would really be great to be able to return a vector of structs or tuples:
For example. I have the following type:
that I want to return in my function
I am getting this error:
My workaround is to flatten my data and return a Vec 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?
The text was updated successfully, but these errors were encountered: