From a311c29f1d4cd0655c8a3ed1497d42036831f50d Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 11 Sep 2018 16:29:25 -0700 Subject: [PATCH 1/2] examples: Add an example of using duck-typed interfaces --- Cargo.toml | 1 + examples/duck-typed-interfaces/.gitignore | 4 ++++ examples/duck-typed-interfaces/Cargo.toml | 10 ++++++++ examples/duck-typed-interfaces/README.md | 15 ++++++++++++ examples/duck-typed-interfaces/build.sh | 15 ++++++++++++ .../duck-typed-interfaces.js | 21 +++++++++++++++++ examples/duck-typed-interfaces/index.html | 9 ++++++++ examples/duck-typed-interfaces/index.js | 3 +++ examples/duck-typed-interfaces/package.json | 10 ++++++++ examples/duck-typed-interfaces/src/lib.rs | 23 +++++++++++++++++++ .../duck-typed-interfaces/webpack.config.js | 10 ++++++++ 11 files changed, 121 insertions(+) create mode 100644 examples/duck-typed-interfaces/.gitignore create mode 100644 examples/duck-typed-interfaces/Cargo.toml create mode 100644 examples/duck-typed-interfaces/README.md create mode 100755 examples/duck-typed-interfaces/build.sh create mode 100644 examples/duck-typed-interfaces/duck-typed-interfaces.js create mode 100644 examples/duck-typed-interfaces/index.html create mode 100644 examples/duck-typed-interfaces/index.js create mode 100644 examples/duck-typed-interfaces/package.json create mode 100755 examples/duck-typed-interfaces/src/lib.rs create mode 100644 examples/duck-typed-interfaces/webpack.config.js diff --git a/Cargo.toml b/Cargo.toml index 059f471e..6f93abd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ members = [ "examples/closures", "examples/comments", "examples/console_log", + "examples/duck-typed-interfaces", "examples/dom", "examples/fetch", "examples/guide-supported-types-examples", diff --git a/examples/duck-typed-interfaces/.gitignore b/examples/duck-typed-interfaces/.gitignore new file mode 100644 index 00000000..2a3ddc15 --- /dev/null +++ b/examples/duck-typed-interfaces/.gitignore @@ -0,0 +1,4 @@ +package-lock.json +rust_duck_typed_interfaces.js +rust_duck_typed_interfaces_bg.js +rust_duck_typed_interfaces_bg.wasm diff --git a/examples/duck-typed-interfaces/Cargo.toml b/examples/duck-typed-interfaces/Cargo.toml new file mode 100644 index 00000000..6fce01f5 --- /dev/null +++ b/examples/duck-typed-interfaces/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rust-duck-typed-interfaces" +version = "0.1.0" +authors = ["The wasm-bindgen Developers"] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = { path = "../.." } diff --git a/examples/duck-typed-interfaces/README.md b/examples/duck-typed-interfaces/README.md new file mode 100644 index 00000000..a5cf3773 --- /dev/null +++ b/examples/duck-typed-interfaces/README.md @@ -0,0 +1,15 @@ +# Canvas 2D Example + +This directory is an example of using the `web-sys` crate to draw on a 2D +canvas. + +You can build and run the example with: + +``` +$ ./build.sh +``` + +(or running the commands on Windows manually) + +and then opening up `http://localhost:8080/` in a web browser should show a +smiley face drawn on canvas by Rust and WebAssembly. diff --git a/examples/duck-typed-interfaces/build.sh b/examples/duck-typed-interfaces/build.sh new file mode 100755 index 00000000..3476b4c1 --- /dev/null +++ b/examples/duck-typed-interfaces/build.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# For more coments about what's going on here, see the `hello_world` example + +set -ex +cd "$(dirname $0)" + +cargo +nightly build --target wasm32-unknown-unknown + +cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml \ + --bin wasm-bindgen -- \ + ../../target/wasm32-unknown-unknown/debug/rust_duck_typed_interfaces.wasm --out-dir . + +npm install +npm run serve diff --git a/examples/duck-typed-interfaces/duck-typed-interfaces.js b/examples/duck-typed-interfaces/duck-typed-interfaces.js new file mode 100644 index 00000000..df7c5ef5 --- /dev/null +++ b/examples/duck-typed-interfaces/duck-typed-interfaces.js @@ -0,0 +1,21 @@ +import { make_em_quack_to_this } from "./rust_duck_typed_interfaces"; + +// All of these objects implement the `Quacks` interface! + +const alex = { + quack: () => "you're not wrong..." +}; + +const ashley = { + quack: () => "" +}; + +const nick = { + quack: () => "rappers I monkey-flip em with the funky rhythm I be kickin" +}; + +// Get all our ducks in a row and call into wasm! + +make_em_quack_to_this(alex); +make_em_quack_to_this(ashley); +make_em_quack_to_this(nick); diff --git a/examples/duck-typed-interfaces/index.html b/examples/duck-typed-interfaces/index.html new file mode 100644 index 00000000..c13b999e --- /dev/null +++ b/examples/duck-typed-interfaces/index.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/examples/duck-typed-interfaces/index.js b/examples/duck-typed-interfaces/index.js new file mode 100644 index 00000000..fe8a21ef --- /dev/null +++ b/examples/duck-typed-interfaces/index.js @@ -0,0 +1,3 @@ +// For more comments about what's going on here, check out the `hello_world` +// example. +import('./duck-typed-interfaces'); diff --git a/examples/duck-typed-interfaces/package.json b/examples/duck-typed-interfaces/package.json new file mode 100644 index 00000000..41eba8c8 --- /dev/null +++ b/examples/duck-typed-interfaces/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "serve": "webpack-dev-server" + }, + "devDependencies": { + "webpack": "^4.11.1", + "webpack-cli": "^2.0.10", + "webpack-dev-server": "^3.1.0" + } +} diff --git a/examples/duck-typed-interfaces/src/lib.rs b/examples/duck-typed-interfaces/src/lib.rs new file mode 100755 index 00000000..dfb6adb8 --- /dev/null +++ b/examples/duck-typed-interfaces/src/lib.rs @@ -0,0 +1,23 @@ +extern crate wasm_bindgen; +use wasm_bindgen::prelude::*; + +/// Here is a duck-typed interface for any JavaScript object that has a `quack` +/// method. +/// +/// Note that any attempts to check if an object is a `Quacks` with +/// `JsCast::is_instance_of` (i.e. the `instanceof` operator) will fail because +/// there is no JS class named `Quacks`. +#[wasm_bindgen] +extern { + pub type Quacks; + + #[wasm_bindgen(structural, method)] + pub fn quack(this: &Quacks) -> String; +} + +/// Next, we can export a function that takes any object that quacks: +#[wasm_bindgen] +pub fn make_em_quack_to_this(duck: &Quacks) { + let s = duck.quack(); + // ... +} diff --git a/examples/duck-typed-interfaces/webpack.config.js b/examples/duck-typed-interfaces/webpack.config.js new file mode 100644 index 00000000..dce27149 --- /dev/null +++ b/examples/duck-typed-interfaces/webpack.config.js @@ -0,0 +1,10 @@ +const path = require('path'); + +module.exports = { + entry: './index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', + }, + mode: 'development' +}; From 1872e84a8ad4ef12f648813beab7bd97aadc5d6f Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 11 Sep 2018 16:29:44 -0700 Subject: [PATCH 2/2] guide: Add section on working with duck-typed interfaces --- examples/duck-typed-interfaces/README.md | 5 ++--- guide/src/SUMMARY.md | 1 + ...cessing-properties-of-untyped-js-values.md | 4 ++++ .../working-with-duck-typed-interfaces.md | 20 +++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 guide/src/reference/working-with-duck-typed-interfaces.md diff --git a/examples/duck-typed-interfaces/README.md b/examples/duck-typed-interfaces/README.md index a5cf3773..5cd195e5 100644 --- a/examples/duck-typed-interfaces/README.md +++ b/examples/duck-typed-interfaces/README.md @@ -1,7 +1,6 @@ -# Canvas 2D Example +# Duck-Typed Interfaces Example -This directory is an example of using the `web-sys` crate to draw on a 2D -canvas. +This directory is an example of using duck-typed JS interfaces with `wasm-bindgen`. You can build and run the example with: diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 016c5341..ee461f01 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -15,6 +15,7 @@ - [No ES Modules](./reference/no-esm.md) - [Arbitrary Data with Serde](./reference/arbitrary-data-with-serde.md) - [Accessing Properties of Untyped JS Values](./reference/accessing-properties-of-untyped-js-values.md) + - [Working with Duck-Typed Interfaces](./reference/working-with-duck-typed-interfaces.md) - [Command Line Interface](./reference/cli.md) - [Supported Types](./reference/types.md) - [Imported JavaScript Types](./reference/types/imported-js-types.md) diff --git a/guide/src/reference/accessing-properties-of-untyped-js-values.md b/guide/src/reference/accessing-properties-of-untyped-js-values.md index 20a18027..5acd128c 100644 --- a/guide/src/reference/accessing-properties-of-untyped-js-values.md +++ b/guide/src/reference/accessing-properties-of-untyped-js-values.md @@ -5,6 +5,10 @@ regardless if it is an `instanceof` some JavaScript class or not, use [the `js_sys::Reflect` APIs][js-sys-reflect]. These APIs are bindings to the [JavaScript builtin `Reflect` object][mdn-reflect] and its methods. +You might also benefit from [using duck-typed +interfaces](./working-with-duck-typed-interfaces.html) instead of working with +untyped values. + ## Reading Properties with `js_sys::Reflect::get` [API documentation for `js_sys::Reflect::get`.](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Reflect.html#method.get) diff --git a/guide/src/reference/working-with-duck-typed-interfaces.md b/guide/src/reference/working-with-duck-typed-interfaces.md new file mode 100644 index 00000000..a6bf2e7b --- /dev/null +++ b/guide/src/reference/working-with-duck-typed-interfaces.md @@ -0,0 +1,20 @@ +# Working with Duck-Typed Interfaces + +Liberal use of [the `structural` +attribute](./attributes/on-js-imports/structural.html) on imported methods, +getters, and setters allows you to define duck-typed interfaces. A duck-typed +interface is one where many different JavaScript objects that don't share the +same base class in their prototype chain and therefore are not `instanceof` the +same base can be used the same way. + +## Defining a Duck-Typed Interface in Rust + +```rust +{{#include ../../../examples/duck-typed-interfaces/src/lib.rs}} +``` + +## JavaScript Usage + +```js +{{#include ../../../examples/duck-typed-interfaces/duck-typed-interfaces.js}} +```