Merge pull request #661 from fitzgen/guide-closures-and-attributes

Guide: closures and attributes
This commit is contained in:
Nick Fitzgerald 2018-08-07 11:15:51 -07:00 committed by GitHub
commit 6d4d9150cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 631 additions and 346 deletions

View File

@ -9,12 +9,29 @@
- [What Just Happened?](./whirlwind-tour/what-just-happened.md)
- [What Else Can We Do?](./whirlwind-tour/what-else-can-we-do.md)
- [Reference](./reference/index.md)
- [Closures](./reference/closures.md)
- [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)
- [Command Line Interface](./reference/cli.md)
- [Supported Types](./reference/types.md)
- [`#[wasm_bindgen]` Attributes](./reference/attributes/index.md)
- [On JavaScript Imports](./reference/attributes/on-js-imports/index.md)
- [`catch`](./reference/attributes/on-js-imports/catch.md)
- [`constructor`](./reference/attributes/on-js-imports/constructor.md)
- [`getter` and `setter`](./reference/attributes/on-js-imports/getter-and-setter.md)
- [`js_class = "Blah"`](./reference/attributes/on-js-imports/js_class.md)
- [`js_name`](./reference/attributes/on-js-imports/js_name.md)
- [`js_namespace`](./reference/attributes/on-js-imports/js_namespace.md)
- [`method`](./reference/attributes/on-js-imports/method.md)
- [`module = "blah"`](./reference/attributes/on-js-imports/module.md)
- [`static_method_of = Blah`](./reference/attributes/on-js-imports/static_method_of.md)
- [`structural`](./reference/attributes/on-js-imports/structural.md)
- [On Rust Exports](./reference/attributes/on-rust-exports/index.md)
- [`constructor`](./reference/attributes/on-rust-exports/constructor.md)
- [`js_name = Blah`](./reference/attributes/on-rust-exports/js_name.md)
- [`readonly`](./reference/attributes/on-rust-exports/readonly.md)
--------------------------------------------------------------------------------
@ -23,10 +40,8 @@
- [JS Objects in Rust](./design/js-objects-in-rust.md)
- [Exporting a function to JS](./design/exporting-rust.md)
- [Exporting a struct to JS](./design/exporting-rust-struct.md)
- [Customizing exports](./design/export-customization.md)
- [Importing a function from JS](./design/importing-js.md)
- [Importing a class from JS](./design/importing-js-struct.md)
- [Customizing imports](./design/import-customization.md)
- [Rust Type conversions](./design/rust-type-conversions.md)
- [Types in `wasm-bindgen`](./design/describe.md)
- [`js-sys`](./js-sys.md)

View File

@ -1,85 +0,0 @@
# Customizing import behavior
The `#[wasm_bindgen]` macro supports a good amount of configuration for
controlling precisely how exports are exported and what they generate in JS.
This section is intended to hopefully be an exhaustive reference of the
possibilities!
* `readonly` - when attached to a `pub` struct field this indicates that it's
readonly from JS and a setter will not be generated.
```rust
#[wasm_bindgen]
pub struct Foo {
pub first: u32,
#[wasm_bindgen(readonly)]
pub second: u32,
}
```
Here the `first` field will be both readable and writable from JS, but the
`second` field will be a `readonly` field in JS where the setter isn't
implemented and attempting to set it will throw an exception.
* `constructor` - when attached to a Rust "constructor" it will make the
generated JS bindings callable as `new Foo()`, for example:
```rust
#[wasm_bindgen]
pub struct Foo {
contents: u32,
}
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(constructor)]
pub fn new() -> Foo {
Foo { contents: 0 }
}
pub fn get_contents(&self) -> u32 {
self.contents
}
}
```
Here this can be used in JS as:
```js
import { Foo } from './my_module';
const f = new Foo();
console.log(f.get_contents());
```
* `js_name` - this can be used to export a different name in JS than what
something is named in Rust, for example:
```rust
#[wasm_bindgen]
pub struct Foo {
contents: u32,
}
#[wasm_bindgen(js_name = makeFoo)]
pub fn make_foo() -> Foo {
Foo { contents: 6 }
}
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(js_name = getContents)]
pub fn get_contents(&self) -> u32 {
self.contents
}
}
```
Here this can be used in JS as:
```js
import { makeFoo } from './my_module';
const foo = makeFoo();
console.log(foo.getContents());
```

View File

@ -1,193 +0,0 @@
# Customizing import behavior
The `#[wasm_bindgen]` macro supports a good amount of configuration for
controlling precisely how imports are imported and what they map to in JS. This
section is intended to hopefully be an exhaustive reference of the
possibilities!
* `catch` - this attribute allows catching a JS exception. This can be attached
to any imported function and the function must return a `Result` where the
`Err` payload is a `JsValue`, like so:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(catch)]
fn foo() -> Result<(), JsValue>;
}
```
If the imported function throws an exception then `Err` will be returned with
the exception that was raised, and otherwise `Ok` is returned with the result
of the function.
By default `wasm-bindgen` will take no action when wasm calls a JS function
which ends up throwing an exception. The wasm spec right now doesn't support
stack unwinding and as a result Rust code **will not execute destructors**.
This can unfortunately cause memory leaks in Rust right now, but as soon as
wasm implements catching exceptions we'll be sure to add support as well!
* `constructor` - this is used to indicate that the function being bound should
actually translate to a `new` constructor in JS. The final argument must be a
type that's imported from JS, and it's what'll get used in JS:
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(constructor)]
fn new() -> Foo;
}
```
This will attach the `new` function to the `Foo` type (implied by
`constructor`) and in JS when this function is called it will be equivalent to
`new Foo()`.
* `method` - this is the gateway to adding methods to imported objects or
otherwise accessing properties on objects via methods and such. This should be
done for doing the equivalent of expressions like `foo.bar()` in JS.
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(method)]
fn work(this: &Foo);
}
```
The first argument of a `method` annotation must be a borrowed reference (not
mutable, shared) to the type that the method is attached to. In this case
we'll be able to call this method like `foo.work()` in JS (where `foo` has
type `Foo`).
In JS this invocation will correspond to accessing `Foo.prototype.work` and
then calling that when the import is called. Note that `method` by default
implies going through `prototype` to get a function pointer.
* `js_namespace` - this attribute indicates that the JS type is accessed through
a particular namespace. For example the `WebAssembly.Module` APIs are all
accessed through the `WebAssembly` namespace. The `js_namespace` can be
applied to any import and whenever the generated JS attempts to reference a
name (like a class or function name) it'll be accessed through this namespace.
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
```
This is an example of how to bind `console.log(x)` in Rust. The `log` function
will be available in the Rust module and will be invoked as `console.log` in
JS.
* `getter` and `setter` - these two attributes can be combined with `method` to
indicate that this is a getter or setter method. A `getter`-tagged function by
default accesses the JS property with the same name as the getter function. A
`setter`'s function name is currently required to start with "set\_" and the
property it accesses is the suffix after "set\_".
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(method, getter)]
fn property(this: &Foo) -> u32;
#[wasm_bindgen(method, setter)]
fn set_property(this: &Foo, val: u32);
}
```
Here we're importing the `Foo` type and defining the ability to access each
object's `property` property. The first function here is a getter and will be
available in Rust as `foo.property()`, and the latter is the setter which is
accessible as `foo.set_property(2)`. Note that both functions have a `this`
argument as they're tagged with `method`.
Finally, you can also pass an argument to the `getter` and `setter`
properties to configure what property is accessed. When the property is
explicitly specified then there is no restriction on the method name. For
example the below is equivalent to the above:
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(method, getter = property)]
fn assorted_method_name(this: &Foo) -> u32;
#[wasm_bindgen(method, setter = "property")]
fn some_other_method_name(this: &Foo, val: u32);
}
```
Properties in JS are accessed through `Object.getOwnPropertyDescriptor`. Note
that this typically only works for class-like-defined properties which aren't
just attached properties on any old object. For accessing any old property on
an object we can use the `structural` flag.
* `indexing_getter`, `indexing_setter` and `indexing_deleter` - these three
attributes can be combined with `method` to indicate that this is a indexing
getter, indexing setter or indexing deleter method. They are different from
`getter` and `setter` in a way that `getter` and `setter` can only access
properties that have a name corresponding to the function name or their
argument, but `indexing_getter`, `indexing_setter` and `indexing_deleter`
work in a dynamic manner, similarly to the indexing syntax in JS
(`object[propertyName]`), hence the name. Should always be used together with
the `structural` flag. For example:
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(method, structural, indexing_getter)]
fn get(this: &Foo, prop: &str) -> u32;
#[wasm_bindgen(method, structural, indexing_setter)]
fn set(this: &Foo, prop: &str, val: u32);
#[wasm_bindgen(method, structural, indexing_deleter)]
fn delete(this: &Foo, prop: &str);
}
```
* `structural` - this is a flag to `method` annotations which indicates that the
method being accessed (or property with getters/setters) should be accessed in
a structural fashion. For example methods are *not* accessed through
`prototype` and properties are accessed on the object directly rather than
through `Object.getOwnPropertyDescriptor`.
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(method, structural)]
fn bar(this: &Foo);
#[wasm_bindgen(method, getter, structural)]
fn baz(this: &Foo) -> u32;
}
```
The type here, `Foo`, is not required to exist in JS (it's not referenced).
Instead wasm-bindgen will generate shims that will access the passed in JS
value's `bar` property to or the `baz` property (depending on the function).
* `js_name = foo` - this can be used to bind to a different function in JS than
the identifier that's defined in Rust. For example you can also define
multiple signatures for a polymorphic function in JS as well:
```rust
#[wasm_bindgen]
extern {
type Foo;
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_string(s: &str);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(n: u32);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: u32, b: JsValue);
}
```
All of these functions will call `console.log` in JS, but each identifier
will have only one signature in Rust.

View File

@ -0,0 +1,6 @@
# `#[wasm_bindgen]` Attributes
The `#[wasm_bindgen]` macro supports a good amount of configuration for
controlling precisely how exports are exported, how imports are imported, and
what the generated JavaScript glue ends up looking like. This section is an
exhaustive reference of the possibilities!

View File

@ -0,0 +1,29 @@
# `catch`
The `catch` attribute allows catching a JavaScript exception. This can be
attached to any imported function or method, and the function must return a
`Result` where the `Err` payload is a `JsValue`:
```rust
#[wasm_bindgen]
extern {
// `catch` on a standalone function.
#[wasm_bindgen(catch)]
fn foo() -> Result<(), JsValue>;
// `catch` on a method.
type Zoidberg;
#[wasm_bindgen(catch, method)]
fn woop_woop_woop(this: &Zoidberg) -> Result<u32, JsValue>;
}
```
If calling the imported function throws an exception, then `Err` will be
returned with the exception that was raised. Otherwise, `Ok` is returned with
the result of the function.
> By default `wasm-bindgen` will take no action when wasm calls a JS function
> which ends up throwing an exception. The wasm spec right now doesn't support
> stack unwinding and as a result Rust code **will not execute destructors**.
> This can unfortunately cause memory leaks in Rust right now, but as soon as
> wasm implements catching exceptions we'll be sure to add support as well!

View File

@ -0,0 +1,24 @@
# `constructor`
The `constructor` attribute is used to indicate that the function being bound
should actually translate to calling the `new` operator in JavaScript. The final
argument must be a type that's imported from JavaScript, and it's what will get
used in the generated glue:
```rust
#[wasm_bindgen]
extern {
type Shoes;
#[wasm_bindgen(constructor)]
fn new() -> Shoes;
}
```
This will attach a `new` static method to the `Shoes` type, and in JavaScript
when this method is called, it will be equivalent to `new Shoes()`.
```rust
// Become a cobbler; construct `new Shoes()`
let shoes = Shoes::new();
```

View File

@ -0,0 +1,78 @@
# `getter` and `setter`
These two attributes can be combined with `method` to indicate that this is a
getter or setter method. A `getter`-tagged function by default accesses the
JavaScript property with the same name as the getter function. A `setter`'s
function name is currently required to start with `set_` and the property it
accesses is the suffix after `set\_`.
Consider the following JavaScript class that has a getter and setter for the
`white_russians` property:
```js
class TheDude {
get white_russians() {
...
}
set white_russians(val) {
...
}
}
```
We would import this with the following `#[wasm_bindgen]` attributes:
```rust
#[wasm_bindgen]
extern {
type TheDude;
#[wasm_bindgen(method, getter)]
fn white_russians(this: &TheDude) -> u32;
#[wasm_bindgen(method, setter)]
fn set_white_russians(this: &TheDude, val: u32);
}
```
Here we're importing the `TheDude` type and defining the ability to access each
object's `white_russians` property. The first function here is a getter and will
be available in Rust as `the_dude.white_russians()`, and the latter is the
setter which is accessible as `the_dude.set_white_russians(2)`. Note that both
functions have a `this` argument as they're tagged with `method`.
Finally, you can also pass an argument to the `getter` and `setter`
properties to configure what property is accessed. When the property is
explicitly specified then there is no restriction on the method name. For
example the below is equivalent to the above:
```rust
#[wasm_bindgen]
extern {
type TheDude;
#[wasm_bindgen(method, getter = white_russians)]
fn my_custom_getter_name(this: &TheDude) -> u32;
#[wasm_bindgen(method, setter = white_russians)]
fn my_custom_setter_name(this: &TheDude, val: u32);
}
```
Heads up! `getter` and `setter` functions are found on the constructor's
prototype chain once at load time, cached, and then the cached accessor is
invoked on each access. If you need to dynamically walk the prototype chain on
every access, add the `structural` attribute!
```js
// This is the default function Rust will invoke on `the_dude.white_russians()`:
const white_russians = Object.getOwnPropertyDescriptor(
TheDude.prototype,
"white_russians"
).get;
// This is what you get by adding `structural`:
const white_russians = function(the_dude) {
return the_dude.white_russians;
};
```

View File

@ -0,0 +1,5 @@
# `#[wasm_bindgen]` on JavaScript Imports
This section enumerates the attributes available for customizing bindings for
JavaScript functions and classes imported into Rust within an `extern { ... }`
block.

View File

@ -0,0 +1,20 @@
# `js_class = "Blah"`
The `js_class` attribute can be used in conjunction with the `method` attribute
to bind methods of imported JavaScript classes that have been renamed on the
Rust side.
```rust
#[wasm_bindgen]
extern {
// We don't want to import JS strings as `String`, since Rust already has a
// `String` type in its prelude, so rename it as `JsString`.
#[wasm_bindgen(js_name = String)]
type JsString;
// This is a method on the JavaScript "String" class, so specify that with
// the `js_class` attribute.
#[wasm_bindgen(method, js_class = "String", js_name = charAt)]
fn char_at(this: &JsString, index: u32) -> JsString;
}
```

View File

@ -0,0 +1,46 @@
# `js_name = blah`
The `js_name` attribute can be used to bind to a different function in
JavaScript than the identifier that's defined in Rust.
Most often, this is used to convert a camel-cased JavaScript identifier into a
snake-cased Rust identifier:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_name = jsOftenUsesCamelCase)]
fn js_often_uses_camel_case() -> u32;
}
```
Sometimes, it is used to bind to JavaScript identifiers that are not valid Rust
identifiers, in which case `js_name = "some string"` is used instead of `js_name
= ident`:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_name = "$$$")]
fn cash_money() -> u32;
}
```
However, you can also use `js_name` to define multiple signatures for
polymorphic JavaScript functions:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn console_log_str(s: &str);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn console_log_u32(n: u32);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn console_log_many(a: u32, b: &JsValue);
}
```
All of these functions will call `console.log` in JavaScript, but each
identifier will have only one signature in Rust.

View File

@ -0,0 +1,21 @@
# `js_namespace = blah`
This attribute indicates that the JavaScript type is accessed through the given
namespace. For example, the `WebAssembly.Module` APIs are all accessed through
the `WebAssembly` namespace. `js_namespace` can be applied to any import
(function or type) and whenever the generated JavaScript attempts to reference a
name (like a class or function name) it'll be accessed through this namespace.
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
log("hello, console!");
```
This is an example of how to bind `console.log` in Rust. The `log` function will
be available in the Rust module and will be invoked as `console.log` in
JavaScript.

View File

@ -0,0 +1,26 @@
# `method`
The `method` attribute allows you to describe methods of imported JavaScript
objects. It is applied on a function that has `this` as its first parameter,
which is a shared reference to an imported JavaScript type.
```rust
#[wasm_bindgen]
extern {
type Set;
#[wasm_bindgen(method)]
fn has(this: &Set, element: &JsValue) -> bool;
}
```
This generates a `has` method on `Set` in Rust, which invokes the
`Set.prototype.has` method in JavaScript.
```rust
let set: Set = ...;
let elem: JsValue = ...;
if set.has(&elem) {
...
}
```

View File

@ -0,0 +1,33 @@
# `module = "blah"`
The `module` attributes configures the module from which items are imported. For
example,
```rust
#[wasm_bindgen(module = "wu/tang/clan")]
extern {
type ThirtySixChambers;
}
```
generates JavaScript import glue like:
```js
import { ThirtySixChambers } from "wu/tang/clan";
```
If a `module` attribute is not present, then the global scope is used
instead. For example,
```rust
#[wasm_bindgen]
extern {
fn illmatic() -> u32;
}
```
generates JavaScript import glue like:
```js
let illmatic = this.illmatic;
```

View File

@ -0,0 +1,25 @@
# `static_method_of = Blah`
The `static_method_of` attribute allows one to specify that an imported function
is a static method of the given imported JavaScript class. For example, to bind
to JavaScript's `Date.now()` static method, one would use this attribute:
```rust
#[wasm_bindgen]
extern {
type Date;
#[wasm_bindgen(static_method_of = Date)]
pub fn now() -> f64;
}
```
The `now` function becomes a static method of the imported type in the Rust
bindings as well:
```rust
let instant = Date::now();
```
This is similar to the `js_namespace` attribute, but the usage from within Rust
is different since the method also becomes a static method of the imported type.

View File

@ -0,0 +1,50 @@
# `structural`
The `structural` flag can be added to `method` annotations, indicating that the
method being accessed (or property with getters/setters) should be accessed in a
structural, duck-type-y fashion. Rather than walking the constructor's prototype
chain once at load time and caching the property result, the prototype chain is
dynamically walked on every access.
```rust
#[wasm_bindgen]
extern {
type Duck;
#[wasm_bindgen(method, structural)]
fn quack(this: &Duck);
#[wasm_bindgen(method, getter, structural)]
fn is_swimming(this: &Duck) -> bool;
}
```
The constructor for the type here, `Duck`, is not required to exist in
JavaScript (it's not referenced). Instead `wasm-bindgen` will generate shims
that will access the passed in JavaScript value's `quack` method or its
`is_swimming` property.
```js
// Without `structural`, get the method directly off the prototype at load time:
const Duck_prototype_quack = Duck.prototype.quack;
function quack(duck) {
Duck_prototype_quack.call(duck);
}
// With `structural`, walk the prototype chain on every access:
function quack(duck) {
duck.quack();
}
```
## Why don't we always use the `structural` behavior?
In theory, it is faster since the prototype chain doesn't need to be traversed
every time the method or property is accessed, but today's optimizing JIT
compilers are really good about eliminating that cost. The real reason is to be
future compatible with the ["host bindings" proposal][host-bindings], which
requires that there be no JavaScript shim between the caller and the native host
function. In this scenario, the properties and methods *must* be resolved before
the wasm is instantiated.
[host-bindings]: https://github.com/WebAssembly/host-bindings/blob/master/proposals/host-bindings/Overview.md

View File

@ -0,0 +1,34 @@
# `constructor`
When attached to a Rust "constructor" it will make the generated JavaScript
bindings callable as `new Foo()`.
For example, consider this exported Rust type and `constructor` annotation:
```rust
#[wasm_bindgen]
pub struct Foo {
contents: u32,
}
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(constructor)]
pub fn new() -> Foo {
Foo { contents: 0 }
}
pub fn get_contents(&self) -> u32 {
self.contents
}
}
```
This can be used in JavaScript as:
```js
import { Foo } from './my_module';
const f = new Foo();
console.log(f.get_contents());
```

View File

@ -0,0 +1,4 @@
# `#[wasm_bindgen]` on Rust Exports
This section enumerates the attributes available for customizing bindings for
Rust functions and `struct`s exported to JavaScript.

View File

@ -0,0 +1,24 @@
# `js_name = Blah`
The `js_name` attribute can be used to export a different name in JS than what
something is named in Rust. It can be applied to both exported Rust functions
and types.
For example, this is often used to convert between Rust's snake-cased
identifiers into JavaScript's camel-cased identifiers:
```rust
#[wasm_bindgen(js_name = doTheThing)]
pub fn do_the_thing() -> u32 {
42
}
```
This can be used in JavaScript as:
```js
import { doTheThing } from './my_module';
const x = doTheThing();
console.log(x);
```

View File

@ -0,0 +1,39 @@
# `readonly`
When attached to a `pub` struct field this indicates that it's read-only from
JavaScript, and a setter will not be generated and exported to JavaScript.
```rust
#[wasm_bindgen]
pub fn make_foo() -> Foo {
Foo {
first: 10,
second: 20,
}
}
#[wasm_bindgen]
pub struct Foo {
pub first: u32,
#[wasm_bindgen(readonly)]
pub second: u32,
}
```
Here the `first` field will be both readable and writable from JS, but the
`second` field will be a `readonly` field in JS where the setter isn't
implemented and attempting to set it will throw an exception.
```js
import { make_foo } from "./my_module";
const foo = make_foo();
// Can both get and set `first`.
foo.first = 99;
console.log(foo.first);
// Can only get `second`.
console.log(foo.second);
```

View File

@ -1,65 +0,0 @@
# Closures
The `#[wasm_bindgen]` attribute supports some Rust closures being passed to JS.
Examples of what you can do are:
```rust
#[wasm_bindgen]
extern {
fn foo(a: &Fn()); // could also be `&mut FnMut()`
}
```
Here a function `foo` is imported from JS where the first argument is a *stack
closure*. You can call this function with a `&Fn()` argument and JS will receive
a JS function. When the `foo` function returns, however, the JS function will be
invalidated and any future usage of it will raise an exception.
Closures also support arguments and return values like exports do, for example:
```rust
#[wasm_bindgen]
extern {
type Foo;
fn bar(a: &Fn(u32, String) -> Foo);
}
```
Sometimes the stack behavior of these closures is not desired. For example you'd
like to schedule a closure to be run on the next turn of the event loop in JS
through `setTimeout`. For this you want the imported function to return but the
JS closure still needs to be valid!
To support this use case you can do:
```rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn baz(a: &Closure<Fn()>);
}
```
The `Closure` type is defined in the `wasm_bindgen` crate and represents a "long
lived" closure. The JS closure passed to `baz` is still valid after `baz`
returns, and the validity of the JS closure is tied to the lifetime of the
`Closure` in Rust. Once `Closure` is dropped it will deallocate its internal
memory and invalidate the corresponding JS function.
Like stack closures a `Closure` also supports `FnMut`:
```rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn another(a: &Closure<FnMut() -> u32>);
}
```
At this time you cannot [pass a JS closure to Rust][cbjs], you can only pass a
Rust closure to JS in limited circumstances.
[cbjs]: https://github.com/rustwasm/wasm-bindgen/issues/103

View File

@ -0,0 +1,118 @@
# Passing Rust Closures to Imported JavaScript Functions
The `#[wasm_bindgen]` attribute supports Rust closures being passed to
JavaScript in two variants:
1. Stack-lifetime closures that should not be invoked by JavaScript again after
the imported JavaScript function that the closure was passed to returns.
2. Heap-allocated closures that can be invoked any number of times, but must be
explicitly deallocated when finished.
## Stack-Lifetime Closures
Closures with a stack lifetime are passed to JavaScript as either `&Fn` or `&mut
FnMut` trait objects:
```rust
// Import JS functions that take closures
#[wasm_bindgen]
extern {
fn takes_immutable_closure(f: &Fn());
fn takes_mutable_closure(f: &mut FnMut());
}
// Usage
takes_immutable_closure(&|| {
// ...
});
let mut times_called = 0;
takes_mutable_closure(&mut || {
times_called += 1;
});
```
**Once these imported functions return, the closures that were given to them
will become invalidated, and any future attempts to call those closures from
JavaScript will raise an exception.**
Closures also support arguments and return values like exports do, for example:
```rust
#[wasm_bindgen]
extern {
fn takes_closure_that_takes_int_and_returns_string(x: &Fn(u32) -> String);
}
takes_closure_that_takes_int_and_returns_string(&|x: u32| -> String {
format!("x is {}", x)
});
```
## Heap-Allocated Closures
Sometimes the discipline of stack-lifetime closures is not desired. For example,
you'd like to schedule a closure to be run on the next turn of the event loop in
JavaScript through `setTimeout`. For this, you want the imported function to
return but the JavaScript closure still needs to be valid!
For this scenario, you need the `Closure` type, which is defined in the
`wasm_bindgen` crate, exported in `wasm_bindgen::prelude`, and represents a
"long lived" closure.
The validity of the JavaScript closure is tied to the lifetime of the `Closure`
in Rust. **Once a `Closure` is dropped, it will deallocate its internal memory
and invalidate the corresponding JavaScript function so that any further
attempts to invoke it raise an exception.**
Like stack closures a `Closure` supports both `Fn` and `FnMut` closures, as well
as arguments and returns.
```rust
#[wasm_bindgen]
extern {
fn setInterval(closure: &Closure<FnMut()>, millis: u32) -> f64;
fn cancelInterval(token: f64);
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub struct Interval {
closure: Closure<FnMut()>,
token: f64,
}
impl Interval {
pub fn new<F>(millis: u32, f: F) -> Interval
where
F: FnMut()
{
// Construct a new closure.
let closure = Closure::new(f);
// Pass the closuer to JS, to run every n milliseconds.
let token = setInterval(&closure, millis);
Interval { closure, token }
}
}
// When the Interval is destroyed, cancel its `setInterval` timer.
impl Drop for Interval {
fn drop(&mut self) {
cancelInterval(self.token);
}
}
// Keep logging "hello" every second until the resulting `Interval` is dropped.
#[wasm_bindgen]
pub fn hello() -> Interval {
Interval::new(1_000, || log("hello"));
}
```

View File

@ -0,0 +1,31 @@
# Receiving JavaScript Closures in Exported Rust Functions
You can use the `js-sys` crate to access JavaScript's `Function` type, and
invoke that function via `Function.prototype.apply` and
`Function.prototype.call`.
For example, we can wrap a `Vec<u32>` in a new type, export it to JavaScript,
and invoke a JavaScript closure on each member of the `Vec`:
```rust
extern crate js_sys;
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct VecU32 {
xs: Vec<u32>,
}
#[wasm_bindgen]
impl VecU32 {
pub fn each(&self, f: &js_sys::Function) {
let this = JsValue::NULL;
for x in &self.xs {
let x = JsValue::from(x);
let _ = f.call1(&this, &x);
}
}
}
```