How can a Vec be returned as a typed array with wasm-bindgen?
Asked Answered
I

1

9

I have a Vec I would like to return and convert to a typed array with wasm-bindgen, ie, to turn a Vec<u32> into a Uint32Array. From my research it appears that wasm-bindgen cannot handle automatically converting these by itself right now (like it does for String) and instead you must use the js-sys crate. I haven't found clear examples of how to use this crate however. It would be much appreciated if a clear simple example of how to use it could be provided.

For completeness' sake, it would be great if answers could explain both how to expose a function returning a Vec<u32>, as well as a struct member, ie, how do you convert these definitions into something that will work:

#[wasm_bindgen]
pub fn my_func() -> Vec<u32> {
    inner_func() // returns Vec<u32>
}

#[wasm_bindgen]
pub struct my_struct {
    #[wasm_bindgen(readonly)]
    pub my_vec: Vec<u32>,
}
Ita answered 20/10, 2020 at 23:19 Comment(6)
Could you please explain what was the problem with js-sys? It's the regular crate so you can add it to the list of your crate's dependencies and then use it in crate's code.Topee
@Topee Yes, I know how to install it, I'm asking how to use it.Ita
I don't understand your question. You can use it like any other regular crate. For example you can create an array: let array = js_sys::Uint32Array::new_with_length(42); then put your values inside: array.fill(13, 0, 41); and finally return it from your function.Topee
@Topee Please read the question again, I don't know how it could possibly be any clearer. I'm asking how to take a Vec<u32> from somewhere else and expose it through wasm-bindgen. Using js_sys::Uint32Array from the ground up may work for some, but I'm asking how to convert a Vec in this question.Ita
Got you. Please take a look at the updated answer.Topee
@Topee Sorry if the question seemed really basic, and I guess it is, but I looked for examples quite a lot without finding anything I could make sense of. And it's good to have a reference question for this even if it is so basic.Ita
T
4

You can convert a Vec<u32> to a js_sys::Uint32Array. So your my_func would look like:

#[wasm_bindgen]
pub fn my_func() -> js_sys::Uint32Array {
    let rust_array = inner_func();
    return js_sys::Uint32Array::from(&rust_array[..]);
}

And the struct can be exposed by making a getter:

#[wasm_bindgen]
pub struct my_struct {
    // Note: not pub
    my_vec: Vec<u32>,
}

#[wasm_bindgen]
impl my_struct {
    #[wasm_bindgen(getter)]
    pub fn my_vec(&self) -> js_sys::Uint32Array {
        return js_sys::Uint32Array::from(&self.my_vec[..]);
    }
}
Topee answered 20/10, 2020 at 23:51 Comment(7)
So what stopping you from creating an instance of Uint32Array?Topee
Ah thank you! This will be slow for long arrays however. Is there a way to copy the whole thing at once?Ita
@curiousdannii, please check the new version :-)Topee
Excellent! :D That's just what I was hoping would be possible. It ends up being quite concise.Ita
By the way, a slice is a reference to values, so you don't "convert" something to a slice, rather you "create" a slice. You can create a slice of the entire vector using &inner_func()[..] and it doesn't need to be stored in an intermediate variable, so the entire body of the function can be reduced to just this line: js_sys::Uint32Array::from(&inner_func()[..]) Note that return and the semicolon are also unnecessary.Glottalized
Does the solution incur a copy? If so, is there a way to expose a view into the WASM heap instead?Bernina
I don't know about a way to expose a view into the WASM heap. It may be difficult to guarantee validity of the view anyway.Topee

© 2022 - 2024 — McMap. All rights reserved.