diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 85e840ecb..41f0b03b2 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -12,8 +12,7 @@ - [Passing Rust Closures to JS](./reference/passing-rust-closures-to-js.md) - [Receiving JS Closures in Rust](./reference/receiving-js-closures-in-rust.md) - [No ES Modules](./reference/no-esm.md) - - [Passing Arbitrary data](./reference/passing-data.md) - - [Feature Reference](./reference/feature-reference.md) + - [Arbitrary Data with Serde](./reference/arbitrary-data-with-serde.md) - [Command Line Interface](./reference/cli.md) - [Supported Types](./reference/types.md) - [`#[wasm_bindgen]` Attributes](./reference/attributes/index.md) diff --git a/guide/src/reference/arbitrary-data-with-serde.md b/guide/src/reference/arbitrary-data-with-serde.md new file mode 100644 index 000000000..a8eb7065a --- /dev/null +++ b/guide/src/reference/arbitrary-data-with-serde.md @@ -0,0 +1,107 @@ +# Serializing and Deserializing Arbitrary Data Into and From `JsValue` with Serde + +It's possible to pass arbirtrary data from Rust to JavaScript by serializing it +with [Serde](https://github.com/serde-rs/serde). `wasm-bindgen` includes the +`JsValue` type, which streamlines serializing and deserializing. + +## Enable the `"serde-serialize"` Feature + +To enable the `"serde-serialize"` feature, do two things in `Cargo.toml`: + +1. Add the `serde` and `serde_derive` crates to `[dependencies]`. +2. Add `features = ["serde-serialize"]` to the existing `wasm-bindgen` + dependency. + +```toml +[dependencies] +serde = "^1.0.59" +serde_derive = "^1.0.59" + +[dependencies.wasm-bindgen] +version = "^0.2" +features = ["serde-serialize"] +``` + +## Import Serde's Custom-Derive Macros + +In your top-level Rust file (e.g. `lib.rs` or `main.rs`), enable the `Serialize` +and `Deserialize` custom-derive macros: + +```rust +#[macro_use] +extern crate serde_derive; +``` + +## Derive the `Serialize` and `Deserialize` Traits + +Add `#![derive(Serialize, Deserialize)]` to your type. All of your type's +members must also be supported by Serde, i.e. their types must also implement +the `Serialize` and `Deserialize` traits. + +For example, let's say we'd like to pass this `struct` to JavaScript; doing so +is not possible in `wasm-bindgen` normally due to the use of `HashMap`s, arrays, +and nested `Vec`s. None of those types are supported for sending across the wasm +ABI naively, but all of them implement Serde's `Serialize` and `Deserialize`. + +Note that we do not need to use the `#[wasm_bindgen]` macro. + +```rust +#[derive(Serialize)] +pub struct Example { + pub field1: HashMap, + pub field2: Vec>, + pub field3: [f32; 4], +} +``` + +## Send it to JavaScript with `JsValue::from_serde` + +Here's a function that will pass an `Example` to JavaScript by serializing it to +`JsValue`: + +```rust +#[wasm_bindgen] +pub fn send_example_to_js() -> JsValue { + let mut field1 = HashMap::new(); + field1.insert(0, String::from("ex")); + let example = Example { + field1, + field2: vec![vec![1., 2.], vec![3., 4.]], + field3: [1., 2., 3., 4.] + }; + + JsValue::from_serde(&example).unwrap() +} +``` + +## Receive it from JavaScript with `JsValue::into_serde` + +Here's a function that will receive a `JsValue` parameter from JavaScript and +then deserialize an `Example` from it: + +```rust +#[wasm_bindgen] +pub fn receive_example_from_js(val: &JsValue) { + let example: Example = val.into_serde().unwrap(); + ... +} +``` + +## JavaScript Usage + +In the `JsValue` that JavaScript gets, `fied1` will be an `Object` (not a +JavaScript `Map`), `field2` will be a JavaScript `Array` whose members are +`Array`s of numbers, and `field3` will be an `Array` of numbers. + +```js +import { send_example_to_js, receive_example_from_js } from "example"; + +// Get the example object from wasm. +let example = send_example_to_js(); + +// Add another "Vec" element to the end of the "Vec>" +example.field2.push([5,6]); + +// Send the example object back to wasm. +receive_example_from_js(example); +``` diff --git a/guide/src/reference/feature-reference.md b/guide/src/reference/feature-reference.md deleted file mode 100644 index 53183ad96..000000000 --- a/guide/src/reference/feature-reference.md +++ /dev/null @@ -1,51 +0,0 @@ -# Feature Reference - -Here this section will attempt to be a reference for the various features -implemented in this project. This is likely not exhaustive but the [tests] -should also be a great place to look for examples. - -[tests]: https://github.com/rustwasm/wasm-bindgen/tree/master/tests - -The `#[wasm_bindgen]` attribute can be attached to functions, structs, -impls, and foreign modules. Impls can only contain functions, and the attribute -cannot be attached to functions in an impl block or functions in a foreign -module. No lifetime parameters or type parameters are allowed on any of these -types. Foreign modules must have the `"C"` abi (or none listed). Free functions -with `#[wasm_bindgen]` might not have the `"C"` abi or none listed, and it's also not -necessary to annotate with the `#[no_mangle]` attribute. - -All structs referenced through arguments to functions should be defined in the -macro itself. Arguments allowed implement the `WasmBoundary` trait, and examples -are: - -* Integers (u64/i64 require `BigInt` support) -* Floats -* Borrowed strings (`&str`) -* Owned strings (`String`) -* Exported structs (`Foo`, annotated with `#[wasm_bindgen]`) -* Exported C-like enums (`Foo`, annotated with `#[wasm_bindgen]`) -* Imported types in a foreign module annotated with `#[wasm_bindgen]` -* Borrowed exported structs (`&Foo` or `&mut Bar`) -* The `JsValue` type and `&JsValue` (not mutable references) -* Vectors and slices of supported integer types and of the `JsValue` type. -* Optional vectors/slices - -All of the above can also be returned except borrowed references. Passing -`Vec` as an argument to a function is not currently supported. Strings are -implemented with shim functions to copy data in/out of the Rust heap. That is, a -string passed to Rust from JS is copied to the Rust heap (using a generated shim -to malloc some space) and then will be freed appropriately. - -Owned values are implemented through boxes. When you return a `Foo` it's -actually turned into `Box>` under the hood and returned to JS as a -pointer. The pointer is to have a defined ABI, and the `RefCell` is to ensure -safety with reentrancy and aliasing in JS. In general you shouldn't see -`RefCell` panics with normal usage. - -JS-values-in-Rust are implemented through indexes that index a table generated -as part of the JS bindings. This table is managed via the ownership specified in -Rust and through the bindings that we're returning. More information about this -can be found in the [design doc]. - -All of these constructs currently create relatively straightforward code on the -JS side of things, mostly having a 1:1 match in Rust with JS. diff --git a/guide/src/reference/passing-data.md b/guide/src/reference/passing-data.md deleted file mode 100644 index c7e00c980..000000000 --- a/guide/src/reference/passing-data.md +++ /dev/null @@ -1,72 +0,0 @@ -# Passing arbitrary data to JS - -It's possible to pass data from Rust to JS not explicitly supported -in the [Feature Reference](./feature-reference.md) by serializing via [Serde](https://github.com/serde-rs/serde). - -`wasm-bindgen` includes the `JsValue` type, which streamlines serializing and deserializing. - -In order accomplish this, we must include the serde and serde_derive -crates in `Cargo.toml`, and configure `wasm-bindgen` to work with this feature: - -Cargo.toml -```toml -[dependencies] -serde = "^1.0.59" -serde_derive = "^1.0.59" - -[dependencies.wasm-bindgen] -version = "^0.2" -features = ["serde-serialize"] -``` - -In our top-level Rust file (eg `lib.rs` or `main.rs`), we enable the `Serialize` -macro: -```rust -#[macro_use] -extern crate serde_derive; -``` - -The data we pass at all nesting levels must be supported by serde, or be a `struct` or `enum` that -derives the Serialize trait. For example, let's say we'd like to pass this -struct to JS; doing so is not possible in bindgen directly due to the use -of public fields, `HashMap`s, arrays, and nested `Vec`s. Note that we do not -need to use the `#[wasm_bindgen]` macro. - -```rust -#[derive(Serialize)] -pub struct Example { - pub field1: HashMap, - pub field2: Vec>, - pub field3: [f32; 4], -} -``` - -Here's a function that will pass an instance of this `struct` to JS: -```rust -#[wasm_bindgen] -pub fn pass_example() -> JsValue { - let mut field1 = HashMap::new(); - field1.insert(0, String::from("ex")); - let example = Example { - field1, - field2: vec![vec![1., 2.], vec![3., 4.]], - field3: [1., 2., 3., 4.] - }; - - JsValue::from_serde(&example).unwrap() -} -``` - -When calling this function from JS, its output will automatically be deserialized. -In this example, `fied1` will be a JS `object` (Not a JS `Map`), `field2` will be a -2d JS `array`, and `field3` will be a 1d JS `array`. Example calling code: - -```typescript -const rust = import("./from_rust"); - -rust.then( - r => { - console.log(r.pass_example()) - } -) -``` \ No newline at end of file