mirror of https://github.com/yewstack/yew
244 lines
12 KiB
Plaintext
244 lines
12 KiB
Plaintext
---
|
||
title: wasm-bindgen
|
||
sidebar_label: wasm-bindgen
|
||
slug: /concepts/wasm-bindgen
|
||
---
|
||
|
||
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) is a library and tool to facilitate
|
||
high-level interactions between Wasm modules and JavaScript; it is built with Rust by
|
||
[The Rust and WebAssembly Working Group](https://rustwasm.github.io/).
|
||
|
||
Yew builds off `wasm-bindgen` and specifically uses the following of its crates:
|
||
|
||
- [`js-sys`](https://crates.io/crates/js-sys)
|
||
- [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||
- [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||
- [`web-sys`](https://crates.io/crates/web-sys)
|
||
|
||
This section will explore some of these crates in a high level in order to make it easier to understand
|
||
and use `wasm-bindgen` APIs with Yew. For a more in-depth guide into `wasm-bindgen` and it's associated
|
||
crates then check out [The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/).
|
||
|
||
For documentation on the above crates check out [`wasm-bindgen docs.rs`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html).
|
||
|
||
:::tip
|
||
Use the `wasm-bindgen` doc.rs search to find browser APIs and JavaScript types that have been imported
|
||
over using `wasm-bindgen`.
|
||
:::
|
||
|
||
## [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||
|
||
This crate provides many of the building blocks for the rest of the crates above. In this section we
|
||
are only going to cover two main areas of the `wasm-bindgen` crate and that is the macro and some
|
||
types / traits you will see pop up again and again.
|
||
|
||
### `#[wasm_bindgen]` macro
|
||
|
||
The `#[wasm_bindgen]` macro, in a high level view, is your translator between Rust and JavaScript, it
|
||
allows you to describe imported JavaScript types in terms of Rust and vice versa. Using this macro
|
||
is more advanced, and you shouldn't need to reach for it unless you are trying to interop with an
|
||
external JavaScript library. The `js-sys` and `web-sys` crates are essentially imported types using
|
||
this macro for JavaScript types and the browser API respectively.
|
||
|
||
Let's go over a simple example of using the `#[wasm-bindgen]` macro to import some specific flavours
|
||
of the [`console.log`](https://developer.mozilla.org/en-US/docs/Web/API/Console/log).
|
||
|
||
```rust ,no_run
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// First up let's take a look of binding `console.log` manually, without the
|
||
// help of `web_sys`. Here we're writing the `#[wasm_bindgen]` annotations
|
||
// manually ourselves, and the correctness of our program relies on the
|
||
// correctness of these annotations!
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
|
||
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
||
// `log(..)`
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
|
||
// The `console.log` is quite polymorphic, so we can bind it with multiple
|
||
// signatures. Note that we need to use `js_name` to ensure we always call
|
||
// `log` in JS.
|
||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||
fn log_u32(a: u32);
|
||
|
||
// Multiple arguments too!
|
||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||
fn log_many(a: &str, b: &str);
|
||
}
|
||
|
||
// using the imported functions!
|
||
log("Hello from Rust!");
|
||
log_u32(42);
|
||
log_many("Logging", "many values!");
|
||
```
|
||
|
||
_This example was adapted from [1.2 Using console.log of The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/examples/console-log.html)_.
|
||
|
||
### Simulating inheritance
|
||
|
||
Inheritance between JavaScript classes is a big part of the language and is a major part of how the
|
||
Document Object Model (DOM). When types are imported using `wasm-bindgen` you can
|
||
also add attributes that describe its inheritance.
|
||
|
||
In Rust this inheritance is simulated using the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)
|
||
and [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) traits. An example of this
|
||
might help; so say you have three types `A`, `B`, and `C` where `C` extends `B` which in turn
|
||
extends `A`.
|
||
When importing these types the `#[wasm-bindgen]` macro will implement the `Deref` and `AsRef`
|
||
traits in the following way:
|
||
|
||
- `C` can `Deref` to `B`
|
||
- `B` can `Deref` to `A`
|
||
- `C` can be `AsRef` to `B`
|
||
- Both `C` & `B` can be `AsRef` to `A`
|
||
|
||
These implementations allow you to call a method from `A` on an instance of `C` and to use `C` as if
|
||
it was `&B` or `&A`.
|
||
|
||
Its important to note that every single type imported using `#[wasm-bindgen]` has the same root type,
|
||
you can think of it as the `A` in the example above, this type is [`JsValue`](#jsvalue) which has
|
||
its own section
|
||
below.
|
||
|
||
_[extends section in The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_
|
||
|
||
### [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)
|
||
|
||
This is a representation of an object owned by JavaScript, this is a root catch-all type for `wasm-bindgen`.
|
||
Any type that comes from `wasm-bindgen` is a `JsValue` and this is because JavaScript doesn't have
|
||
a strong type system so any function that accepts a variable `x` doesn't define its type so `x` can be
|
||
a valid JavaScript value; hence `JsValue`. So when you are working with imported functions or types that
|
||
accept a `JsValue`, then any imported value is _technically_ valid.
|
||
|
||
`JsValue` can be accepted by a function but that function may still only expect certain types and this
|
||
can lead to panics - so when using raw `wasm-bindgen` APIs check the documentation of the JavaScript
|
||
being imported whether an exception will be caused if that value is not a certain type.
|
||
|
||
_[`JsValue` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)._
|
||
|
||
### [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)
|
||
|
||
Rust has a strong type system and JavaScript...doesn't 😞 So in order for Rust to maintain these
|
||
strong types but still be convenient the web assembly group came up with a pretty neat trait `JsCast`.
|
||
Its job is to help you move from one JavaScript "type" to another, which sounds vague, but it means
|
||
that if you have one type which you know is really another then you can use the functions of `JsCast`
|
||
to jump from one type to the other. It's a nice trait to get to know when working with `web-sys`,
|
||
`wasm_bindgen`, `js-sys` - you'll notice lots of types will implement `JsCast` from those crates.
|
||
|
||
`JsCast` provides both checked and unchecked methods of casting - so that at runtime if you are
|
||
unsure what type a certain object is you can try to cast it which returns possible failure types like
|
||
[`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) and
|
||
[`Result`](https://doc.rust-lang.org/std/result/enum.Result.html).
|
||
|
||
A common example of this in [`web-sys`](wasm-bindgen/web-sys) is when you are trying to get the
|
||
target of an event, you might know what the target element is but the
|
||
[`web_sys::Event`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html) API will always return an [`Option<web_sys::EventTarget>`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target)
|
||
so you will need to cast it to the element type. so you can call its methods.
|
||
|
||
```rust
|
||
// need to import the trait.
|
||
use wasm_bindgen::JsCast;
|
||
use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement};
|
||
|
||
fn handle_event(event: Event) {
|
||
let target: EventTarget = event
|
||
.target()
|
||
.expect("I'm sure this event has a target!");
|
||
|
||
// maybe the target is a select element?
|
||
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
|
||
// do something amazing here
|
||
return;
|
||
}
|
||
|
||
// if it wasn't a select element then I KNOW it's a input element!
|
||
let input_element: HtmlInputElement = target.unchecked_into();
|
||
}
|
||
```
|
||
|
||
The [`dyn_ref`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_ref)
|
||
method is a checked cast that returns an `Option<&T>` which means the original type
|
||
can be used again if the cast failed and thus returned `None`. The
|
||
[`dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into)
|
||
method will consume `self`, as per convention for into methods in Rust, and the type returned is
|
||
`Result<T, Self>` this means if the casting fails then the value in `Err` is so you can try again
|
||
or do something else with the original type.
|
||
|
||
_[`JsCast` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)._
|
||
|
||
### [`Closure`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)
|
||
|
||
The `Closure` type provides a way to transfer Rust closures to JavaScript, the closures past to
|
||
JavaScript must have a `'static` lifetime for soundness reasons.
|
||
|
||
This type is a "handle" in the sense that whenever it is dropped it will invalidate the JS
|
||
closure that it refers to. Any usage of the closure in JS after the Closure has been dropped will
|
||
raise an exception.
|
||
|
||
`Closure` is often used when you are working with a `js-sys` or `web-sys` API that accepts a type
|
||
[`&js_sys::Function`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Function.html).
|
||
An example of using a `Closure` in Yew can be found in the [Using `Closure` section](html/events#using-closure-verbose) on the [Events](html/events) page.
|
||
|
||
_[`Closure` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)._
|
||
|
||
## [`js-sys`](https://crates.io/crates/js-sys)
|
||
|
||
The `js-sys` crate provides bindings / imports of JavaScript's standard, built-in objects, including
|
||
their methods and properties.
|
||
|
||
This does not include any web APIs as this is what [`web-sys`](wasm-bindgen/web-sys) is for!
|
||
|
||
_[`js-sys` documentation](https://rustwasm.github.io/wasm-bindgen/api/js_sys/index.html)._
|
||
|
||
## [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||
|
||
The `wasm-bindgen-futures` crate provides a bridge for working with JavaScript Promise types as a
|
||
Rust Future, and similarly contains utilities to turn a rust Future into a JavaScript Promise.
|
||
This can be useful when working with asynchronous or otherwise blocking work in Rust (wasm),
|
||
and provides the ability to interoperate with JavaScript events and JavaScript I/O primitives.
|
||
|
||
There are three main interfaces in this crate currently:
|
||
|
||
1. [`JsFuture`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/struct.JsFuture.html) -
|
||
A type that is constructed with a [`Promise`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Promise.html)
|
||
and can then be used as a `Future<Output=Result<JsValue, JsValue>>`. This Rust future will resolve
|
||
or reject with the value coming out of the `Promise`.
|
||
|
||
2. [`future_to_promise`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) -
|
||
Converts a Rust `Future<Output=Result<JsValue, JsValue>>` into a
|
||
JavaScript `Promise`. The future’s result will translate to either a resolved or rejected
|
||
`Promise` in JavaScript.
|
||
|
||
3. [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) -
|
||
Spawns a `Future<Output = ()>` on the current thread. This is the best way
|
||
to run a Future in Rust without sending it to JavaScript.
|
||
|
||
_[`wasm-bindgen-futures` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/index.html)._
|
||
|
||
### [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)
|
||
|
||
`spawn_local` is going to be the most commonly used part of the `wasm-bindgen-futures` crate in Yew
|
||
as this helps when using libraries that have async APIs.
|
||
|
||
```rust ,no_run
|
||
use web_sys::console;
|
||
use wasm_bindgen_futures::spawn_local;
|
||
|
||
async fn my_async_fn() -> String { String::from("Hello") }
|
||
|
||
spawn_local(async {
|
||
let mut string = my_async_fn().await;
|
||
string.push_str(", world!");
|
||
// console log "Hello, world!"
|
||
console::log_1(&string.into());
|
||
});
|
||
```
|
||
|
||
Yew has also added support for futures in certain APIs, most notably you can create a
|
||
`callback_future` which accepts an `async` block - this uses `spawn_local` internally.
|
||
|
||
_[`spawn_local` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)._
|