mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-15 17:50:51 +00:00
6962 lines
328 KiB
HTML
6962 lines
328 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="en" class="sidebar-visible no-js">
|
||
<head>
|
||
<!-- Book generated using mdBook -->
|
||
<meta charset="UTF-8">
|
||
<title>The `wasm-bindgen` Guide</title>
|
||
|
||
<meta name="robots" content="noindex" />
|
||
|
||
|
||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||
<meta name="description" content="">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#ffffff" />
|
||
|
||
<link rel="shortcut icon" href="favicon.png">
|
||
<link rel="stylesheet" href="css/variables.css">
|
||
<link rel="stylesheet" href="css/general.css">
|
||
<link rel="stylesheet" href="css/chrome.css">
|
||
<link rel="stylesheet" href="css/print.css" media="print">
|
||
|
||
<!-- Fonts -->
|
||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||
|
||
<!-- Highlight.js Stylesheets -->
|
||
<link rel="stylesheet" href="highlight.css">
|
||
<link rel="stylesheet" href="tomorrow-night.css">
|
||
<link rel="stylesheet" href="ayu-highlight.css">
|
||
|
||
<!-- Custom theme stylesheets -->
|
||
|
||
|
||
|
||
</head>
|
||
<body class="light">
|
||
<!-- Provide site root to javascript -->
|
||
<script type="text/javascript">
|
||
var path_to_root = "";
|
||
var default_theme = "light";
|
||
</script>
|
||
|
||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||
<script type="text/javascript">
|
||
try {
|
||
var theme = localStorage.getItem('mdbook-theme');
|
||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||
|
||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||
}
|
||
|
||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||
}
|
||
} catch (e) { }
|
||
</script>
|
||
|
||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||
<script type="text/javascript">
|
||
var theme;
|
||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||
document.body.className = theme;
|
||
document.querySelector('html').className = theme + ' js';
|
||
</script>
|
||
|
||
<!-- Hide / unhide sidebar before it is displayed -->
|
||
<script type="text/javascript">
|
||
var html = document.querySelector('html');
|
||
var sidebar = 'hidden';
|
||
if (document.body.clientWidth >= 1080) {
|
||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||
sidebar = sidebar || 'visible';
|
||
}
|
||
html.classList.remove('sidebar-visible');
|
||
html.classList.add("sidebar-" + sidebar);
|
||
</script>
|
||
|
||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||
<div class="sidebar-scrollbox">
|
||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li class="spacer"></li><li><a href="examples/index.html"><strong aria-hidden="true">1.</strong> Examples</a></li><li><ol class="section"><li><a href="examples/hello-world.html"><strong aria-hidden="true">1.1.</strong> Hello, World!</a></li><li><a href="examples/console-log.html"><strong aria-hidden="true">1.2.</strong> Using console.log</a></li><li><a href="examples/add.html"><strong aria-hidden="true">1.3.</strong> Small wasm files</a></li><li><a href="examples/without-a-bundler.html"><strong aria-hidden="true">1.4.</strong> Without a Bundler</a></li><li><a href="examples/wasm2js.html"><strong aria-hidden="true">1.5.</strong> Converting WebAssembly to JS</a></li><li><a href="examples/import-js.html"><strong aria-hidden="true">1.6.</strong> Importing functions from JS</a></li><li><a href="examples/char.html"><strong aria-hidden="true">1.7.</strong> Working with char</a></li><li><a href="examples/wasm-in-wasm.html"><strong aria-hidden="true">1.8.</strong> js-sys: WebAssembly in WebAssembly</a></li><li><a href="examples/dom.html"><strong aria-hidden="true">1.9.</strong> web-sys: DOM hello world</a></li><li><a href="examples/closures.html"><strong aria-hidden="true">1.10.</strong> web-sys: Closures</a></li><li><a href="examples/performance.html"><strong aria-hidden="true">1.11.</strong> web-sys: performance.now</a></li><li><a href="examples/fetch.html"><strong aria-hidden="true">1.12.</strong> web-sys: using fetch</a></li><li><a href="examples/2d-canvas.html"><strong aria-hidden="true">1.13.</strong> web-sys: canvas hello world</a></li><li><a href="examples/julia.html"><strong aria-hidden="true">1.14.</strong> web-sys: canvas Julia set</a></li><li><a href="examples/web-audio.html"><strong aria-hidden="true">1.15.</strong> web-sys: WebAudio</a></li><li><a href="examples/webgl.html"><strong aria-hidden="true">1.16.</strong> web-sys: WebGL</a></li><li><a href="examples/websockets.html"><strong aria-hidden="true">1.17.</strong> web-sys: WebSockets</a></li><li><a href="examples/webrtc_datachannel.html"><strong aria-hidden="true">1.18.</strong> web-sys: WebRTC DataChannel</a></li><li><a href="examples/request-animation-frame.html"><strong aria-hidden="true">1.19.</strong> web-sys: requestAnimationFrame</a></li><li><a href="examples/paint.html"><strong aria-hidden="true">1.20.</strong> web-sys: A Simple Paint Program</a></li><li><a href="examples/raytrace.html"><strong aria-hidden="true">1.21.</strong> Parallel Raytracing</a></li><li><a href="examples/todomvc.html"><strong aria-hidden="true">1.22.</strong> web-sys: A TODO MVC App</a></li></ol></li><li><a href="reference/index.html"><strong aria-hidden="true">2.</strong> Reference</a></li><li><ol class="section"><li><a href="reference/deployment.html"><strong aria-hidden="true">2.1.</strong> Deployment</a></li><li><a href="reference/js-snippets.html"><strong aria-hidden="true">2.2.</strong> JS snippets</a></li><li><a href="reference/passing-rust-closures-to-js.html"><strong aria-hidden="true">2.3.</strong> Passing Rust Closures to JS</a></li><li><a href="reference/receiving-js-closures-in-rust.html"><strong aria-hidden="true">2.4.</strong> Receiving JS Closures in Rust</a></li><li><a href="reference/js-promises-and-rust-futures.html"><strong aria-hidden="true">2.5.</strong> Promises and Futures</a></li><li><a href="reference/iterating-over-js-values.html"><strong aria-hidden="true">2.6.</strong> Iterating over JS Values</a></li><li><a href="reference/arbitrary-data-with-serde.html"><strong aria-hidden="true">2.7.</strong> Arbitrary Data with Serde</a></li><li><a href="reference/accessing-properties-of-untyped-js-values.html"><strong aria-hidden="true">2.8.</strong> Accessing Properties of Untyped JS Values</a></li><li><a href="reference/working-with-duck-typed-interfaces.html"><strong aria-hidden="true">2.9.</strong> Working with Duck-Typed Interfaces</a></li><li><a href="reference/cli.html"><strong aria-hidden="true">2.10.</strong> Command Line Interface</a></li><li><a href="reference/optimize-size.html"><strong aria-hidden="true">2.11.</strong> Optimizing for Size</a></li><li><a href="reference/rust-targets.html"><strong aria-hidden="true">2.12.</strong> Supported Rust Targets</a></li><li><a href="reference/browser-support.html"><strong aria-hidden="true">2.13.</strong> Supported Browsers</a></li><li><a href="reference/types.html"><strong aria-hidden="true">2.14.</strong> Supported Types</a></li><li><ol class="section"><li><a href="reference/types/imported-js-types.html"><strong aria-hidden="true">2.14.1.</strong> Imported JavaScript Types</a></li><li><a href="reference/types/exported-rust-types.html"><strong aria-hidden="true">2.14.2.</strong> Exported Rust Types</a></li><li><a href="reference/types/jsvalue.html"><strong aria-hidden="true">2.14.3.</strong> JsValue</a></li><li><a href="reference/types/boxed-jsvalue-slice.html"><strong aria-hidden="true">2.14.4.</strong> Box<[JsValue]></a></li><li><a href="reference/types/pointers.html"><strong aria-hidden="true">2.14.5.</strong> *const T and *mut T</a></li><li><a href="reference/types/numbers.html"><strong aria-hidden="true">2.14.6.</strong> Numbers</a></li><li><a href="reference/types/bool.html"><strong aria-hidden="true">2.14.7.</strong> bool</a></li><li><a href="reference/types/char.html"><strong aria-hidden="true">2.14.8.</strong> char</a></li><li><a href="reference/types/str.html"><strong aria-hidden="true">2.14.9.</strong> str</a></li><li><a href="reference/types/string.html"><strong aria-hidden="true">2.14.10.</strong> String</a></li><li><a href="reference/types/number-slices.html"><strong aria-hidden="true">2.14.11.</strong> Number Slices</a></li><li><a href="reference/types/boxed-number-slices.html"><strong aria-hidden="true">2.14.12.</strong> Boxed Number Slices</a></li><li><a href="reference/types/result.html"><strong aria-hidden="true">2.14.13.</strong> Result<T, JsValue></a></li></ol></li><li><a href="reference/attributes/index.html"><strong aria-hidden="true">2.15.</strong> #[wasm_bindgen] Attributes</a></li><li><ol class="section"><li><a href="reference/attributes/on-js-imports/index.html"><strong aria-hidden="true">2.15.1.</strong> On JavaScript Imports</a></li><li><ol class="section"><li><a href="reference/attributes/on-js-imports/catch.html"><strong aria-hidden="true">2.15.1.1.</strong> catch</a></li><li><a href="reference/attributes/on-js-imports/constructor.html"><strong aria-hidden="true">2.15.1.2.</strong> constructor</a></li><li><a href="reference/attributes/on-js-imports/extends.html"><strong aria-hidden="true">2.15.1.3.</strong> extends</a></li><li><a href="reference/attributes/on-js-imports/getter-and-setter.html"><strong aria-hidden="true">2.15.1.4.</strong> getter and setter</a></li><li><a href="reference/attributes/on-js-imports/final.html"><strong aria-hidden="true">2.15.1.5.</strong> final</a></li><li><a href="reference/attributes/on-js-imports/indexing-getter-setter-deleter.html"><strong aria-hidden="true">2.15.1.6.</strong> indexing_getter, indexing_setter, and indexing_deleter</a></li><li><a href="reference/attributes/on-js-imports/js_class.html"><strong aria-hidden="true">2.15.1.7.</strong> js_class = "Blah"</a></li><li><a href="reference/attributes/on-js-imports/js_name.html"><strong aria-hidden="true">2.15.1.8.</strong> js_name</a></li><li><a href="reference/attributes/on-js-imports/js_namespace.html"><strong aria-hidden="true">2.15.1.9.</strong> js_namespace</a></li><li><a href="reference/attributes/on-js-imports/method.html"><strong aria-hidden="true">2.15.1.10.</strong> method</a></li><li><a href="reference/attributes/on-js-imports/module.html"><strong aria-hidden="true">2.15.1.11.</strong> module = "blah"</a></li><li><a href="reference/attributes/on-js-imports/raw_module.html"><strong aria-hidden="true">2.15.1.12.</strong> raw_module = "blah"</a></li><li><a href="reference/attributes/on-js-imports/static_method_of.html"><strong aria-hidden="true">2.15.1.13.</strong> static_method_of = Blah</a></li><li><a href="reference/attributes/on-js-imports/structural.html"><strong aria-hidden="true">2.15.1.14.</strong> structural</a></li><li><a href="reference/attributes/on-js-imports/variadic.html"><strong aria-hidden="true">2.15.1.15.</strong> variadic</a></li><li><a href="reference/attributes/on-js-imports/vendor_prefix.html"><strong aria-hidden="true">2.15.1.16.</strong> vendor_prefix</a></li></ol></li><li><a href="reference/attributes/on-rust-exports/index.html"><strong aria-hidden="true">2.15.2.</strong> On Rust Exports</a></li><li><ol class="section"><li><a href="reference/attributes/on-rust-exports/constructor.html"><strong aria-hidden="true">2.15.2.1.</strong> constructor</a></li><li><a href="reference/attributes/on-rust-exports/js_name.html"><strong aria-hidden="true">2.15.2.2.</strong> js_name = Blah</a></li><li><a href="reference/attributes/on-rust-exports/readonly.html"><strong aria-hidden="true">2.15.2.3.</strong> readonly</a></li><li><a href="reference/attributes/on-rust-exports/skip.html"><strong aria-hidden="true">2.15.2.4.</strong> skip</a></li><li><a href="reference/attributes/on-rust-exports/start.html"><strong aria-hidden="true">2.15.2.5.</strong> start</a></li><li><a href="reference/attributes/on-rust-exports/typescript_custom_section.html"><strong aria-hidden="true">2.15.2.6.</strong> typescript_custom_section</a></li><li><a href="reference/attributes/on-rust-exports/getter-and-setter.html"><strong aria-hidden="true">2.15.2.7.</strong> getter and setter</a></li><li><a href="reference/attributes/on-rust-exports/inspectable.html"><strong aria-hidden="true">2.15.2.8.</strong> inspectable</a></li><li><a href="reference/attributes/on-rust-exports/skip_typescript.html"><strong aria-hidden="true">2.15.2.9.</strong> skip_typescript</a></li><li><a href="reference/attributes/on-rust-exports/typescript_type.html"><strong aria-hidden="true">2.15.2.10.</strong> typescript_type</a></li></ol></li></ol></li></ol></li><li><a href="web-sys/index.html"><strong aria-hidden="true">3.</strong> web-sys</a></li><li><ol class="section"><li><a href="web-sys/using-web-sys.html"><strong aria-hidden="true">3.1.</strong> Using web-sys</a></li><li><a href="web-sys/cargo-features.html"><strong aria-hidden="true">3.2.</strong> Cargo Features</a></li><li><a href="web-sys/function-overloads.html"><strong aria-hidden="true">3.3.</strong> Function Overloads</a></li><li><a href="web-sys/type-translations.html"><strong aria-hidden="true">3.4.</strong> Type Translations</a></li><li><a href="web-sys/inheritance.html"><strong aria-hidden="true">3.5.</strong> Inheritance</a></li><li><a href="web-sys/unstable-apis.html"><strong aria-hidden="true">3.6.</strong> Unstable APIs</a></li></ol></li><li><a href="wasm-bindgen-test/index.html"><strong aria-hidden="true">4.</strong> Testing with wasm-bindgen-test</a></li><li><ol class="section"><li><a href="wasm-bindgen-test/usage.html"><strong aria-hidden="true">4.1.</strong> Usage</a></li><li><a href="wasm-bindgen-test/asynchronous-tests.html"><strong aria-hidden="true">4.2.</strong> Writing Asynchronous Tests</a></li><li><a href="wasm-bindgen-test/browsers.html"><strong aria-hidden="true">4.3.</strong> Testing in Headless Browsers</a></li><li><a href="wasm-bindgen-test/continuous-integration.html"><strong aria-hidden="true">4.4.</strong> Continuous Integration</a></li></ol></li><li><a href="contributing/index.html"><strong aria-hidden="true">5.</strong> Contributing to wasm-bindgen</a></li><li><ol class="section"><li><a href="contributing/testing.html"><strong aria-hidden="true">5.1.</strong> Testing</a></li><li><a href="contributing/design/index.html"><strong aria-hidden="true">5.2.</strong> Internal Design</a></li><li><ol class="section"><li><a href="contributing/design/js-objects-in-rust.html"><strong aria-hidden="true">5.2.1.</strong> JS Objects in Rust</a></li><li><a href="contributing/design/exporting-rust.html"><strong aria-hidden="true">5.2.2.</strong> Exporting a function to JS</a></li><li><a href="contributing/design/exporting-rust-struct.html"><strong aria-hidden="true">5.2.3.</strong> Exporting a struct to JS</a></li><li><a href="contributing/design/importing-js.html"><strong aria-hidden="true">5.2.4.</strong> Importing a function from JS</a></li><li><a href="contributing/design/importing-js-struct.html"><strong aria-hidden="true">5.2.5.</strong> Importing a class from JS</a></li><li><a href="contributing/design/rust-type-conversions.html"><strong aria-hidden="true">5.2.6.</strong> Rust Type conversions</a></li><li><a href="contributing/design/describe.html"><strong aria-hidden="true">5.2.7.</strong> Types in wasm-bindgen</a></li></ol></li><li><a href="contributing/js-sys/index.html"><strong aria-hidden="true">5.3.</strong> js-sys</a></li><li><ol class="section"><li><a href="contributing/js-sys/testing.html"><strong aria-hidden="true">5.3.1.</strong> Testing</a></li><li><a href="contributing/js-sys/adding-more-apis.html"><strong aria-hidden="true">5.3.2.</strong> Adding More APIs</a></li></ol></li><li><a href="contributing/web-sys/index.html"><strong aria-hidden="true">5.4.</strong> web-sys</a></li><li><ol class="section"><li><a href="contributing/web-sys/overview.html"><strong aria-hidden="true">5.4.1.</strong> Overview</a></li><li><a href="contributing/web-sys/testing.html"><strong aria-hidden="true">5.4.2.</strong> Testing</a></li><li><a href="contributing/web-sys/logging.html"><strong aria-hidden="true">5.4.3.</strong> Logging</a></li><li><a href="contributing/web-sys/supporting-more-web-apis.html"><strong aria-hidden="true">5.4.4.</strong> Supporting More Web APIs</a></li></ol></li><li><a href="contributing/publishing.html"><strong aria-hidden="true">5.5.</strong> Publishing</a></li><li><a href="contributing/team.html"><strong aria-hidden="true">5.6.</strong> Team</a></li></ol></li></ol>
|
||
</div>
|
||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||
</nav>
|
||
|
||
<div id="page-wrapper" class="page-wrapper">
|
||
|
||
<div class="page">
|
||
<style>
|
||
header.warning {
|
||
background-color: rgb(242, 222, 222);
|
||
border-bottom-color: rgb(238, 211, 215);
|
||
border-bottom-left-radius: 4px;
|
||
border-bottom-right-radius: 4px;
|
||
border-bottom-style: solid;
|
||
border-bottom-width: 0.666667px;
|
||
border-image-outset: 0 0 0 0;
|
||
border-image-repeat: stretch stretch;
|
||
border-image-slice: 100% 100% 100% 100%;
|
||
border-image-source: none;
|
||
border-image-width: 1 1 1 1;
|
||
border-left-color: rgb(238, 211, 215);
|
||
border-left-style: solid;
|
||
border-left-width: 0.666667px;
|
||
border-right-color: rgb(238, 211, 215);
|
||
border-right-style: solid;
|
||
border-right-width: 0.666667px;
|
||
border-top-color: rgb(238, 211, 215);
|
||
border-top-left-radius: 4px;
|
||
border-top-right-radius: 4px;
|
||
border-top-style: solid;
|
||
border-top-width: 0.666667px;
|
||
color: rgb(185, 74, 72);
|
||
margin-bottom: 0px;
|
||
margin-left: 0px;
|
||
margin-right: 0px;
|
||
margin-top: 30px;
|
||
padding-bottom: 8px;
|
||
padding-left: 14px;
|
||
padding-right: 35px;
|
||
padding-top: 8px;
|
||
text-align: center;
|
||
}
|
||
</style>
|
||
<header class='warning'>
|
||
This is the <strong>unpublished</strong> documentation of
|
||
<code>wasm-bindgen</code>, the published documentation is available
|
||
<a href="https://rustwasm.github.io/docs/wasm-bindgen/">
|
||
on the main Rust and WebAssembly documentation site
|
||
</a>. Features documented here may not be available in released versions of
|
||
<code>wasm-bindgen</code>.
|
||
</header>
|
||
|
||
<div id="menu-bar" class="menu-bar">
|
||
<div id="menu-bar-sticky-container">
|
||
<div class="left-buttons">
|
||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||
<i class="fa fa-bars"></i>
|
||
</button>
|
||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||
<i class="fa fa-paint-brush"></i>
|
||
</button>
|
||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||
</ul>
|
||
|
||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||
<i class="fa fa-search"></i>
|
||
</button>
|
||
|
||
</div>
|
||
|
||
<h1 class="menu-title">The `wasm-bindgen` Guide</h1>
|
||
|
||
<div class="right-buttons">
|
||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||
<i id="print-button" class="fa fa-print"></i>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div id="search-wrapper" class="hidden">
|
||
<form id="searchbar-outer" class="searchbar-outer">
|
||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||
</form>
|
||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||
<div id="searchresults-header" class="searchresults-header"></div>
|
||
<ul id="searchresults">
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||
<script type="text/javascript">
|
||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||
});
|
||
</script>
|
||
|
||
<div id="content" class="content">
|
||
<main>
|
||
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1>
|
||
<p>This book is about <code>wasm-bindgen</code>, a Rust library and CLI tool that facilitate
|
||
high-level interactions between wasm modules and JavaScript. The <code>wasm-bindgen</code>
|
||
tool and crate are only one part of the <a href="https://rustwasm.github.io/">Rust and WebAssembly
|
||
ecosystem</a>. If you're not familiar already with <code>wasm-bindgen</code> it's
|
||
recommended to start by reading the <a href="https://rustwasm.github.io/docs/book/">Game of Life tutorial</a>. If you're
|
||
curious about <code>wasm-pack</code>, you can find that <a href="https://rustwasm.github.io/docs/wasm-pack/">documentation here</a>.</p>
|
||
<p>The <code>wasm-bindgen</code> tool is sort of half polyfill for features like the <a href="https://github.com/WebAssembly/host-bindings">host
|
||
bindings proposal</a> and half features for empowering high-level
|
||
interactions between JS and wasm-compiled code (currently mostly from Rust).
|
||
More specifically this project allows JS/wasm to communicate with strings, JS
|
||
objects, classes, etc, as opposed to purely integers and floats. Using
|
||
<code>wasm-bindgen</code> for example you can define a JS class in Rust or take a string
|
||
from JS or return one. The functionality is growing as well!</p>
|
||
<p>Currently this tool is Rust-focused but the underlying foundation is
|
||
language-independent, and it's hoping that over time as this tool stabilizes
|
||
that it can be used for languages like C/C++!</p>
|
||
<p>Notable features of this project includes:</p>
|
||
<ul>
|
||
<li>Importing JS functionality in to Rust such as <a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/dom">DOM manipulation</a>,
|
||
<a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/console_log">console logging</a>, or <a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/performance">performance monitoring</a>.</li>
|
||
<li>Exporting Rust functionality to JS such as classes, functions, etc.</li>
|
||
<li>Working with rich types like strings, numbers, classes, closures, and objects
|
||
rather than simply <code>u32</code> and floats.</li>
|
||
<li>Automatically generating TypeScript bindings for Rust code being consumed by
|
||
JS.</li>
|
||
</ul>
|
||
<p>With the addition of <a href="https://rustwasm.github.io/docs/wasm-pack/"><code>wasm-pack</code></a> you can run the gamut from running Rust on
|
||
the web locally, publishing it as part of a larger application, or even
|
||
publishing Rust-compiled-to-WebAssembly on NPM!</p>
|
||
<h1><a class="header" href="#examples-of-using-wasm-bindgen-js-sys-and-web-sys" id="examples-of-using-wasm-bindgen-js-sys-and-web-sys">Examples of using <code>wasm-bindgen</code>, <code>js-sys</code>, and <code>web-sys</code></a></h1>
|
||
<p>This subsection contains examples of using the <code>wasm-bindgen</code>, <code>js-sys</code>, and
|
||
<code>web-sys</code> crates. Each example should have more information about what it's
|
||
doing.</p>
|
||
<p>These examples all assume familiarity with <code>wasm-bindgen</code>, <code>wasm-pack</code>, and
|
||
building a Rust and WebAssembly project. If you're unfamiliar with these check
|
||
out the <a href="https://rustwasm.github.io/docs/book/">Game of Life tutorial</a> or <a href="https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html">wasm pack tutorials</a> to help you
|
||
get started.</p>
|
||
<p>The source code for all examples can also be <a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples">found online</a> to download
|
||
and run locally. Most examples are configured with Webpack/<code>wasm-pack</code> and can
|
||
be built with <code>npm run serve</code>. Other examples which don't use Webpack are
|
||
accompanied with instructions or a <code>build.sh</code> showing how to build it.</p>
|
||
<p>Note that most examples currently use Webpack to assemble the final output
|
||
artifact, but this is not required! You can review the <a href="examples/../reference/deployment.html">deployment
|
||
documentation</a> for other options of how to deploy Rust and WebAssembly.</p>
|
||
<h1><a class="header" href="#hello-world" id="hello-world">Hello, World!</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/hello_world">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/hello_world/">view the compiled example online</a></p>
|
||
<p>This is the "Hello, world!" example of <code>#[wasm_bindgen]</code> showing how to set up
|
||
a project, export a function to JS, call it from JS, and then call the <code>alert</code>
|
||
function in Rust.</p>
|
||
<h2><a class="header" href="#cargotoml" id="cargotoml"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> lists the <code>wasm-bindgen</code> crate as a dependency.</p>
|
||
<p>Also of note is the <code>crate-type = ["cdylib"]</code> which is largely used for wasm
|
||
final artifacts today.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "hello_world"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs" id="srclibrs"><code>src/lib.rs</code></a></h2>
|
||
<p>Here we define our Rust entry point along with calling the <code>alert</code> function.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
fn alert(s: &str);
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn greet(name: &str) {
|
||
alert(&format!("Hello, {}!", name));
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#indexjs" id="indexjs"><code>index.js</code></a></h2>
|
||
<p>Our JS entry point is quite small!</p>
|
||
<pre><code class="language-js">// Note that a dynamic `import` statement here is required due to
|
||
// webpack/webpack#6615, but in theory `import { greet } from './pkg';`
|
||
// will work here one day as well!
|
||
const rust = import('./pkg');
|
||
|
||
rust
|
||
.then(m => m.greet('World!'))
|
||
.catch(console.error);
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#webpack-specific-files" id="webpack-specific-files">Webpack-specific files</a></h2>
|
||
<blockquote>
|
||
<p><strong>Note</strong>: Webpack is not required for this example, and if you're interested
|
||
in options that don't use a JS bundler <a href="examples/without-a-bundler.html">see other examples</a>.</p>
|
||
</blockquote>
|
||
<p>And finally here's the Webpack configuration and <code>package.json</code> for this
|
||
project:</p>
|
||
<p><strong>webpack.config.js</strong></p>
|
||
<pre><code class="language-js">const path = require('path');
|
||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||
const webpack = require('webpack');
|
||
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||
|
||
module.exports = {
|
||
entry: './index.js',
|
||
output: {
|
||
path: path.resolve(__dirname, 'dist'),
|
||
filename: 'index.js',
|
||
},
|
||
plugins: [
|
||
new HtmlWebpackPlugin(),
|
||
new WasmPackPlugin({
|
||
crateDirectory: path.resolve(__dirname, ".")
|
||
}),
|
||
// Have this example work in Edge which doesn't ship `TextEncoder` or
|
||
// `TextDecoder` at this time.
|
||
new webpack.ProvidePlugin({
|
||
TextDecoder: ['text-encoding', 'TextDecoder'],
|
||
TextEncoder: ['text-encoding', 'TextEncoder']
|
||
})
|
||
],
|
||
mode: 'development'
|
||
};
|
||
|
||
</code></pre>
|
||
<p><strong>package.json</strong></p>
|
||
<pre><code class="language-json">{
|
||
"scripts": {
|
||
"build": "webpack",
|
||
"serve": "webpack-dev-server"
|
||
},
|
||
"devDependencies": {
|
||
"@wasm-tool/wasm-pack-plugin": "1.0.1",
|
||
"text-encoding": "^0.7.0",
|
||
"html-webpack-plugin": "^3.2.0",
|
||
"webpack": "^4.29.4",
|
||
"webpack-cli": "^3.1.1",
|
||
"webpack-dev-server": "^3.1.0"
|
||
}
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#consolelog" id="consolelog"><code>console.log</code></a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/console_log">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/console_log/">view the compiled example online</a></p>
|
||
<p>This example shows off how to use <code>console.log</code> in a variety of ways, all the
|
||
way from bare-bones usage to a <code>println!</code>-like macro with <code>web_sys</code>.</p>
|
||
<h2><a class="header" href="#srclibrs-1" id="srclibrs-1"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() {
|
||
bare_bones();
|
||
using_a_macro();
|
||
using_web_sys();
|
||
}
|
||
|
||
// 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);
|
||
}
|
||
|
||
fn bare_bones() {
|
||
log("Hello from Rust!");
|
||
log_u32(42);
|
||
log_many("Logging", "many values!");
|
||
}
|
||
|
||
// Next let's define a macro that's like `println!`, only it works for
|
||
// `console.log`. Note that `println!` doesn't actually work on the wasm target
|
||
// because the standard library currently just eats all output. To get
|
||
// `println!`-like behavior in your app you'll likely want a macro like this.
|
||
|
||
macro_rules! console_log {
|
||
// Note that this is using the `log` function imported above during
|
||
// `bare_bones`
|
||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||
}
|
||
|
||
fn using_a_macro() {
|
||
console_log!("Hello {}!", "world");
|
||
console_log!("Let's print some numbers...");
|
||
console_log!("1 + 3 = {}", 1 + 3);
|
||
}
|
||
|
||
// And finally, we don't even have to define the `log` function ourselves! The
|
||
// `web_sys` crate already has it defined for us.
|
||
|
||
fn using_web_sys() {
|
||
use web_sys::console;
|
||
|
||
console::log_1(&"Hello using web-sys".into());
|
||
|
||
let js: JsValue = 4.into();
|
||
console::log_2(&"Logging arbitrary values looks like".into(), &js);
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#small-wasm-files" id="small-wasm-files">Small wasm files</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/add">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/add/">view the compiled example online</a></p>
|
||
<p>One of <code>wasm-bindgen</code>'s core goals is a pay-only-for-what-you-use philosophy, so
|
||
if we don't use much then we shouldn't be paying much! As a result
|
||
<code>#[wasm_bindgen]</code> can generate super-small executables</p>
|
||
<p>Currently this code...</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn add(a: u32, b: u32) -> u32 {
|
||
a + b
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<p>generates a 710 byte wasm binary:</p>
|
||
<pre><code>$ ls -l add_bg.wasm
|
||
-rw-rw-r-- 1 alex alex 710 Sep 19 17:32 add_bg.wasm
|
||
</code></pre>
|
||
<p>If you run <a href="https://github.com/webassembly/binaryen">wasm-opt</a>, a C++ tool for optimize WebAssembly, you can make it
|
||
even smaller too!</p>
|
||
<pre><code>$ wasm-opt -Os add_bg.wasm -o add.wasm
|
||
$ ls -l add.wasm
|
||
-rw-rw-r-- 1 alex alex 172 Sep 19 17:33 add.wasm
|
||
</code></pre>
|
||
<p>And sure enough, using the <a href="https://github.com/webassembly/wabt">wasm2wat</a> tool it's quite small!</p>
|
||
<pre><code>$ wasm2wat add.wasm
|
||
(module
|
||
(type (;0;) (func (param i32 i32) (result i32)))
|
||
(func (;0;) (type 0) (param i32 i32) (result i32)
|
||
get_local 1
|
||
get_local 0
|
||
i32.add)
|
||
(table (;0;) 1 1 anyfunc)
|
||
(memory (;0;) 17)
|
||
(global (;0;) i32 (i32.const 1049118))
|
||
(global (;1;) i32 (i32.const 1049118))
|
||
(export "memory" (memory 0))
|
||
(export "__indirect_function_table" (table 0))
|
||
(export "__heap_base" (global 0))
|
||
(export "__data_end" (global 1))
|
||
(export "add" (func 0))
|
||
(data (i32.const 1049096) "invalid malloc request"))
|
||
</code></pre>
|
||
<p>Also don't forget to compile in release mode for the smallest binaries! For
|
||
larger applications you'll likely also want to turn on LTO to generate the
|
||
smallest binaries:</p>
|
||
<pre><code class="language-toml">[profile.release]
|
||
lto = true
|
||
</code></pre>
|
||
<h1><a class="header" href="#without-a-bundler" id="without-a-bundler">Without a Bundler</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/without-a-bundler">View full source code</a></p>
|
||
<p>This example shows how the <code>--target web</code> flag can be used load code in a
|
||
browser directly. For this deployment strategy bundlers like Webpack are not
|
||
required. For more information on deployment see the <a href="examples/../reference/deployment.html">dedicated
|
||
documentation</a>.</p>
|
||
<p>First let's take a look at the code and see how when we're using <code>--target web</code>
|
||
we're not actually losing any functionality!</p>
|
||
<pre><pre class="playpen"><code class="language-rust">use wasm_bindgen::prelude::*;
|
||
|
||
// Called when the wasm module is instantiated
|
||
#[wasm_bindgen(start)]
|
||
pub fn main() -> Result<(), JsValue> {
|
||
// Use `web_sys`'s global `window` function to get a handle on the global
|
||
// window object.
|
||
let window = web_sys::window().expect("no global `window` exists");
|
||
let document = window.document().expect("should have a document on window");
|
||
let body = document.body().expect("document should have a body");
|
||
|
||
// Manufacture the element we're gonna append
|
||
let val = document.create_element("p")?;
|
||
val.set_inner_html("Hello from Rust!");
|
||
|
||
body.append_child(&val)?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn add(a: u32, b: u32) -> u32 {
|
||
a + b
|
||
}
|
||
|
||
</code></pre></pre>
|
||
<p>Otherwise the rest of the deployment magic happens in <code>index.html</code>:</p>
|
||
<pre><code class="language-html"><html>
|
||
<head>
|
||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
||
</head>
|
||
<body>
|
||
<!-- Note the usage of `type=module` here as this is an ES6 module -->
|
||
<script type="module">
|
||
// Use ES module import syntax to import functionality from the module
|
||
// that we have compiled.
|
||
//
|
||
// Note that the `default` import is an initialization function which
|
||
// will "boot" the module and make it ready to use. Currently browsers
|
||
// don't support natively imported WebAssembly as an ES module, but
|
||
// eventually the manual initialization won't be required!
|
||
import init, { add } from './pkg/without_a_bundler.js';
|
||
|
||
async function run() {
|
||
// First up we need to actually load the wasm file, so we use the
|
||
// default export to inform it where the wasm file is located on the
|
||
// server, and then we wait on the returned promise to wait for the
|
||
// wasm to be loaded.
|
||
//
|
||
// It may look like this: `await init('./pkg/without_a_bundler_bg.wasm');`,
|
||
// but there is also a handy default inside `init` function, which uses
|
||
// `import.meta` to locate the wasm file relatively to js file.
|
||
//
|
||
// Note that instead of a string you can also pass in any of the
|
||
// following things:
|
||
//
|
||
// * `WebAssembly.Module`
|
||
//
|
||
// * `ArrayBuffer`
|
||
//
|
||
// * `Response`
|
||
//
|
||
// * `Promise` which returns any of the above, e.g. `fetch("./path/to/wasm")`
|
||
//
|
||
// This gives you complete control over how the module is loaded
|
||
// and compiled.
|
||
//
|
||
// Also note that the promise, when resolved, yields the wasm module's
|
||
// exports which is the same as importing the `*_bg` module in other
|
||
// modes
|
||
await init();
|
||
|
||
// And afterwards we can use all the functionality defined in wasm.
|
||
const result = add(1, 2);
|
||
console.log(`1 + 2 = ${result}`);
|
||
if (result !== 3)
|
||
throw new Error("wasm addition doesn't work!");
|
||
}
|
||
|
||
run();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|
||
</code></pre>
|
||
<p>And that's it! Be sure to read up on the <a href="examples/../reference/deployment.html">deployment options</a> to see
|
||
what it means to deploy without a bundler.</p>
|
||
<h2><a class="header" href="#using-the-older---target-no-modules" id="using-the-older---target-no-modules">Using the older <code>--target no-modules</code></a></h2>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/without-a-bundler">View full source code</a></p>
|
||
<p>The older version of using <code>wasm-bindgen</code> without a bundler is to use the
|
||
<code>--target no-modules</code> flag to the <code>wasm-bindgen</code> CLI.</p>
|
||
<p>While similar to the newer <code>--target web</code>, the <code>--target no-modules</code> flag has a
|
||
few caveats:</p>
|
||
<ul>
|
||
<li>It does not support <a href="examples/../reference/js-snippets.html">local JS snippets</a></li>
|
||
<li>It does not generate an ES module</li>
|
||
</ul>
|
||
<p>With that in mind the main difference is how the wasm/JS code is loaded, and
|
||
here's an example of loading the output of <code>wasm-pack</code> for the same module as
|
||
above.</p>
|
||
<pre><code class="language-html"><html>
|
||
<head>
|
||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
||
</head>
|
||
<body>
|
||
<!-- Include the JS generated by `wasm-pack build` -->
|
||
<script src='pkg/without_a_bundler_no_modules.js'></script>
|
||
|
||
<script>
|
||
// Like with the `--target web` output the exports are immediately
|
||
// available but they won't work until we initialize the module. Unlike
|
||
// `--target web`, however, the globals are all stored on a
|
||
// `wasm_bindgen` global. The global itself is the initialization
|
||
// function and then the properties of the global are all the exported
|
||
// functions.
|
||
//
|
||
// Note that the name `wasm_bindgen` can be configured with the
|
||
// `--no-modules-global` CLI flag
|
||
const { add } = wasm_bindgen;
|
||
|
||
async function run() {
|
||
await wasm_bindgen('./pkg/without_a_bundler_no_modules_bg.wasm');
|
||
|
||
const result = add(1, 2);
|
||
console.log(`1 + 2 = ${result}`);
|
||
}
|
||
|
||
run();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#converting-webassembly-to-js" id="converting-webassembly-to-js">Converting WebAssembly to JS</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/wasm2js">View full source code</a></p>
|
||
<p>Not all browsers have support for <code>WebAssembly</code> at this time (although all major
|
||
ones do). If you'd like to support older browsers, you probably want a method
|
||
that doesn't involve keeping two codebases in sync!</p>
|
||
<p>Thankfully there's a tool from <a href="https://github.com/WebAssembly/binaryen">binaryen</a> called <code>wasm2js</code> to convert a wasm
|
||
file to JS. This JS file, if successfully produced, is equivalent to the wasm
|
||
file (albeit a little bit larger and slower), and can be loaded into practically
|
||
any browser.</p>
|
||
<p>This example is relatively simple (cribbing from the <a href="examples/console-log.html"><code>console.log</code>
|
||
example</a>):</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// lifted from the `console_log` example
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
}
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() {
|
||
log("Hello, World!");
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<p>The real magic happens when you actually build the app. Just after
|
||
<code>wasm-bindgen</code> we see here how we execute <code>wasm2js</code> in our build script:</p>
|
||
<pre><code class="language-sh">#!/bin/sh
|
||
|
||
set -ex
|
||
|
||
# Compile our wasm module and run `wasm-bindgen`
|
||
wasm-pack build --target web
|
||
|
||
# Run the `wasm2js` tool from `binaryen`
|
||
wasm2js pkg/wasm2js_bg.wasm -o pkg/wasm2js_bg.js
|
||
|
||
# Update our JS shim to require the JS file instead
|
||
sed -i 's/wasm2js_bg.wasm/wasm2js_bg.js/' pkg/wasm2js.js
|
||
|
||
http
|
||
|
||
</code></pre>
|
||
<p>Note that the <code>wasm2js</code> tool is still pretty early days so there's likely to be
|
||
a number of bugs to run into or work around. If any are encountered though
|
||
please feel free to report them upstream!</p>
|
||
<p>Also note that eventually this will ideally be automatically done by your
|
||
bundler and no action would be needed from you to work in older browsers via
|
||
<code>wasm2js</code>!</p>
|
||
<h1><a class="header" href="#importing-non-browser-js" id="importing-non-browser-js">Importing non-browser JS</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/import_js">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/import_js/">view the compiled example online</a></p>
|
||
<p>The <code>#[wasm_bindgen]</code> attribute can be used on <code>extern "C" { .. }</code> blocks to import
|
||
functionality from JS. This is how the <code>js-sys</code> and the <code>web-sys</code> crates are
|
||
built, but you can also use it in your own crate!</p>
|
||
<p>For example if you're working with this JS file:</p>
|
||
<pre><code class="language-js">// defined-in-js.js
|
||
export function name() {
|
||
return 'Rust';
|
||
}
|
||
|
||
export class MyClass {
|
||
constructor() {
|
||
this._number = 42;
|
||
}
|
||
|
||
get number() {
|
||
return this._number;
|
||
}
|
||
|
||
set number(n) {
|
||
return this._number = n;
|
||
}
|
||
|
||
render() {
|
||
return `My number is: ${this.number}`;
|
||
}
|
||
}
|
||
|
||
</code></pre>
|
||
<p>you can use it in Rust with:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen(module = "/defined-in-js.js")]
|
||
extern "C" {
|
||
fn name() -> String;
|
||
|
||
type MyClass;
|
||
|
||
#[wasm_bindgen(constructor)]
|
||
fn new() -> MyClass;
|
||
|
||
#[wasm_bindgen(method, getter)]
|
||
fn number(this: &MyClass) -> u32;
|
||
#[wasm_bindgen(method, setter)]
|
||
fn set_number(this: &MyClass, number: u32) -> MyClass;
|
||
#[wasm_bindgen(method)]
|
||
fn render(this: &MyClass) -> String;
|
||
}
|
||
|
||
// lifted from the `console_log` example
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
}
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() {
|
||
log(&format!("Hello from {}!", name())); // should output "Hello from Rust!"
|
||
|
||
let x = MyClass::new();
|
||
assert_eq!(x.number(), 42);
|
||
x.set_number(10);
|
||
log(&x.render());
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<p>You can also <a href="examples/../reference/attributes/on-js-imports/index.html">explore the full list of ways to configure imports</a></p>
|
||
<h1><a class="header" href="#working-with-the-char-type" id="working-with-the-char-type">Working with the <code>char</code> type</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/char">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/char/">view the compiled example online</a></p>
|
||
<p>The <code>#[wasm_bindgen]</code> macro will convert the rust <code>char</code> type to a single
|
||
code-point js <code>string</code>, and this example shows how to work with this.</p>
|
||
<p>Opening this example should display a single counter with a random character
|
||
for it's <code>key</code> and 0 for its <code>count</code>. You can click the <code>+</code> button to increase a
|
||
counter's count. By clicking on the "add counter" button you should see a new
|
||
counter added to the list with a different random character for it's <code>key</code>.</p>
|
||
<p>Under the hood javascript is choosing a random character from an Array of
|
||
characters and passing that to the rust Counter struct's constructor so the
|
||
character you are seeing on the page has made the full round trip from js to
|
||
rust and back to js.</p>
|
||
<h2><a class="header" href="#srclibrs-2" id="srclibrs-2"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// lifted from the `console_log` example
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
#[derive(Debug)]
|
||
pub struct Counter {
|
||
key: char,
|
||
count: i32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl Counter {
|
||
pub fn default() -> Counter {
|
||
log("Counter::default");
|
||
Self::new('a', 0)
|
||
}
|
||
pub fn new(key: char, count: i32) -> Counter {
|
||
log(&format!("Counter::new({}, {})", key, count));
|
||
Counter {
|
||
key: key,
|
||
count: count,
|
||
}
|
||
}
|
||
|
||
pub fn key(&self) -> char {
|
||
log("Counter.key()");
|
||
self.key
|
||
}
|
||
|
||
pub fn count(&self) -> i32 {
|
||
log("Counter.count");
|
||
self.count
|
||
}
|
||
|
||
pub fn increment(&mut self) {
|
||
log("Counter.increment");
|
||
self.count += 1;
|
||
}
|
||
|
||
pub fn update_key(&mut self, key: char) {
|
||
self.key = key;
|
||
}
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#indexjs-1" id="indexjs-1"><code>index.js</code></a></h2>
|
||
<pre><code class="language-js">/* eslint-disable no-unused-vars */
|
||
import { chars } from './chars-list.js';
|
||
let imp = import('./pkg');
|
||
let mod;
|
||
|
||
let counters = [];
|
||
imp
|
||
.then(wasm => {
|
||
mod = wasm;
|
||
addCounter();
|
||
let b = document.getElementById('add-counter');
|
||
if (!b) throw new Error('Unable to find #add-counter');
|
||
b.addEventListener('click', ev => addCounter());
|
||
})
|
||
.catch(console.error);
|
||
|
||
function addCounter() {
|
||
let ctr = mod.Counter.new(randomChar(), 0);
|
||
counters.push(ctr);
|
||
update();
|
||
}
|
||
|
||
function update() {
|
||
let container = document.getElementById('container');
|
||
if (!container) throw new Error('Unable to find #container in dom');
|
||
while (container.hasChildNodes()) {
|
||
if (container.lastChild.id == 'add-counter') break;
|
||
container.removeChild(container.lastChild);
|
||
}
|
||
for (var i = 0; i < counters.length; i++) {
|
||
let counter = counters[i];
|
||
container.appendChild(newCounter(counter.key(), counter.count(), ev => {
|
||
counter.increment();
|
||
update();
|
||
}));
|
||
}
|
||
}
|
||
|
||
function randomChar() {
|
||
console.log('randomChar');
|
||
let idx = Math.floor(Math.random() * (chars.length - 1));
|
||
console.log('index', idx);
|
||
let ret = chars.splice(idx, 1)[0];
|
||
console.log('char', ret);
|
||
return ret;
|
||
}
|
||
|
||
function newCounter(key, value, cb) {
|
||
let container = document.createElement('div');
|
||
container.setAttribute('class', 'counter');
|
||
let title = document.createElement('h1');
|
||
title.appendChild(document.createTextNode('Counter ' + key));
|
||
container.appendChild(title);
|
||
container.appendChild(newField('Count', value));
|
||
let plus = document.createElement('button');
|
||
plus.setAttribute('type', 'button');
|
||
plus.setAttribute('class', 'plus-button');
|
||
plus.appendChild(document.createTextNode('+'));
|
||
plus.addEventListener('click', cb);
|
||
container.appendChild(plus);
|
||
return container;
|
||
}
|
||
|
||
function newField(key, value) {
|
||
let ret = document.createElement('div');
|
||
ret.setAttribute('class', 'field');
|
||
let name = document.createElement('span');
|
||
name.setAttribute('class', 'name');
|
||
name.appendChild(document.createTextNode(key));
|
||
ret.appendChild(name);
|
||
let val = document.createElement('span');
|
||
val.setAttribute('class', 'value');
|
||
val.appendChild(document.createTextNode(value));
|
||
ret.appendChild(val);
|
||
return ret;
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#js-sys-webassembly-in-webassembly" id="js-sys-webassembly-in-webassembly">js-sys: WebAssembly in WebAssembly</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/wasm-in-wasm">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/wasm-in-wasm/">view the compiled example online</a></p>
|
||
<p>Using the <code>js-sys</code> crate we can get pretty meta and instantiate <code>WebAssembly</code>
|
||
modules from inside <code>WebAssembly</code> modules!</p>
|
||
<h2><a class="header" href="#srclibrs-3" id="srclibrs-3"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use js_sys::{Function, Object, Reflect, WebAssembly};
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
||
|
||
// lifted from the `console_log` example
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(a: &str);
|
||
}
|
||
|
||
macro_rules! console_log {
|
||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||
}
|
||
|
||
const WASM: &[u8] = include_bytes!("add.wasm");
|
||
|
||
async fn run_async() -> Result<(), JsValue> {
|
||
console_log!("instantiating a new wasm module directly");
|
||
|
||
let a = JsFuture::from(WebAssembly::instantiate_buffer(WASM, &Object::new())).await?;
|
||
let b: WebAssembly::Instance = Reflect::get(&a, &"instance".into())?.dyn_into()?;
|
||
|
||
let c = b.exports();
|
||
|
||
let add = Reflect::get(c.as_ref(), &"add".into())?
|
||
.dyn_into::<Function>()
|
||
.expect("add export wasn't a function");
|
||
|
||
let three = add.call2(&JsValue::undefined(), &1.into(), &2.into())?;
|
||
console_log!("1 + 2 = {:?}", three);
|
||
let mem = Reflect::get(c.as_ref(), &"memory".into())?
|
||
.dyn_into::<WebAssembly::Memory>()
|
||
.expect("memory export wasn't a `WebAssembly.Memory`");
|
||
console_log!("created module has {} pages of memory", mem.grow(0));
|
||
console_log!("giving the module 4 more pages of memory");
|
||
mem.grow(4);
|
||
console_log!("now the module has {} pages of memory", mem.grow(0));
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() {
|
||
spawn_local(async {
|
||
run_async().await.unwrap_throw();
|
||
});
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#web-sys-dom-hello-world" id="web-sys-dom-hello-world">web-sys: DOM hello world</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/dom">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/dom/">view the compiled example online</a></p>
|
||
<p>Using <code>web-sys</code> we're able to interact with all the standard web platform
|
||
methods, including those of the DOM! Here we take a look at a simple "Hello,
|
||
world!" which manufactures a DOM element in Rust, customizes it, and then
|
||
appends it to the page.</p>
|
||
<h2><a class="header" href="#cargotoml-1" id="cargotoml-1"><code>Cargo.toml</code></a></h2>
|
||
<p>You can see here how we depend on <code>web-sys</code> and activate associated features to
|
||
enable all the various APIs:</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "dom"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'Document',
|
||
'Element',
|
||
'HtmlElement',
|
||
'Node',
|
||
'Window',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-4" id="srclibrs-4"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// Called by our JS entry point to run the example
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() -> Result<(), JsValue> {
|
||
// Use `web_sys`'s global `window` function to get a handle on the global
|
||
// window object.
|
||
let window = web_sys::window().expect("no global `window` exists");
|
||
let document = window.document().expect("should have a document on window");
|
||
let body = document.body().expect("document should have a body");
|
||
|
||
// Manufacture the element we're gonna append
|
||
let val = document.create_element("p")?;
|
||
val.set_inner_html("Hello from Rust!");
|
||
|
||
body.append_child(&val)?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#web-sys-closures" id="web-sys-closures">web-sys: Closures</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/closures">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/closures/">view the compiled example online</a></p>
|
||
<p>One of the features of <code>#[wasm_bindgen]</code> is that you can pass closures defined
|
||
in Rust off to JS. This can be a bit tricky at times, though, so the example
|
||
here shows how to interact with some standard web APIs with closures.</p>
|
||
<h2><a class="header" href="#srclibrs-5" id="srclibrs-5"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use js_sys::{Array, Date};
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
use web_sys::{Document, Element, HtmlElement, Window};
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() -> Result<(), JsValue> {
|
||
let window = web_sys::window().expect("should have a window in this context");
|
||
let document = window.document().expect("window should have a document");
|
||
|
||
// One of the first interesting things we can do with closures is simply
|
||
// access stack data in Rust!
|
||
let array = Array::new();
|
||
array.push(&"Hello".into());
|
||
array.push(&1.into());
|
||
let mut first_item = None;
|
||
array.for_each(&mut |obj, idx, _arr| match idx {
|
||
0 => {
|
||
assert_eq!(obj, "Hello");
|
||
first_item = obj.as_string();
|
||
}
|
||
1 => assert_eq!(obj, 1),
|
||
_ => panic!("unknown index: {}", idx),
|
||
});
|
||
assert_eq!(first_item, Some("Hello".to_string()));
|
||
|
||
// Below are some more advanced usages of the `Closure` type for closures
|
||
// that need to live beyond our function call.
|
||
|
||
setup_clock(&window, &document)?;
|
||
setup_clicker(&document);
|
||
|
||
// And now that our demo is ready to go let's switch things up so
|
||
// everything is displayed and our loading prompt is hidden.
|
||
document
|
||
.get_element_by_id("loading")
|
||
.expect("should have #loading on the page")
|
||
.dyn_ref::<HtmlElement>()
|
||
.expect("#loading should be an `HtmlElement`")
|
||
.style()
|
||
.set_property("display", "none")?;
|
||
document
|
||
.get_element_by_id("script")
|
||
.expect("should have #script on the page")
|
||
.dyn_ref::<HtmlElement>()
|
||
.expect("#script should be an `HtmlElement`")
|
||
.style()
|
||
.set_property("display", "block")?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
// Set up a clock on our page and update it each second to ensure it's got
|
||
// an accurate date.
|
||
//
|
||
// Note the usage of `Closure` here because the closure is "long lived",
|
||
// basically meaning it has to persist beyond the call to this one function.
|
||
// Also of note here is the `.as_ref().unchecked_ref()` chain, which is how
|
||
// you can extract `&Function`, what `web-sys` expects, from a `Closure`
|
||
// which only hands you `&JsValue` via `AsRef`.
|
||
fn setup_clock(window: &Window, document: &Document) -> Result<(), JsValue> {
|
||
let current_time = document
|
||
.get_element_by_id("current-time")
|
||
.expect("should have #current-time on the page");
|
||
update_time(&current_time);
|
||
let a = Closure::wrap(Box::new(move || update_time(&current_time)) as Box<dyn Fn()>);
|
||
window
|
||
.set_interval_with_callback_and_timeout_and_arguments_0(a.as_ref().unchecked_ref(), 1000)?;
|
||
fn update_time(current_time: &Element) {
|
||
current_time.set_inner_html(&String::from(
|
||
Date::new_0().to_locale_string("en-GB", &JsValue::undefined()),
|
||
));
|
||
}
|
||
|
||
// The instance of `Closure` that we created will invalidate its
|
||
// corresponding JS callback whenever it is dropped, so if we were to
|
||
// normally return from `setup_clock` then our registered closure will
|
||
// raise an exception when invoked.
|
||
//
|
||
// Normally we'd store the handle to later get dropped at an appropriate
|
||
// time but for now we want it to be a global handler so we use the
|
||
// `forget` method to drop it without invalidating the closure. Note that
|
||
// this is leaking memory in Rust, so this should be done judiciously!
|
||
a.forget();
|
||
|
||
Ok(())
|
||
}
|
||
|
||
// We also want to count the number of times that our green square has been
|
||
// clicked. Our callback will update the `#num-clicks` div.
|
||
//
|
||
// This is pretty similar above, but showing how closures can also implement
|
||
// `FnMut()`.
|
||
fn setup_clicker(document: &Document) {
|
||
let num_clicks = document
|
||
.get_element_by_id("num-clicks")
|
||
.expect("should have #num-clicks on the page");
|
||
let mut clicks = 0;
|
||
let a = Closure::wrap(Box::new(move || {
|
||
clicks += 1;
|
||
num_clicks.set_inner_html(&clicks.to_string());
|
||
}) as Box<dyn FnMut()>);
|
||
document
|
||
.get_element_by_id("green-square")
|
||
.expect("should have #green-square on the page")
|
||
.dyn_ref::<HtmlElement>()
|
||
.expect("#green-square be an `HtmlElement`")
|
||
.set_onclick(Some(a.as_ref().unchecked_ref()));
|
||
|
||
// See comments in `setup_clock` above for why we use `a.forget()`.
|
||
a.forget();
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#web-sys-performancenow" id="web-sys-performancenow">web-sys: <code>performance.now</code></a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/performance">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/performance/">view the compiled example online</a></p>
|
||
<p>Want to profile some Rust code in the browser? No problem! You can use the
|
||
<code>performance.now()</code> API and friends to get timing information to see how long
|
||
things take.</p>
|
||
<h2><a class="header" href="#srclibrs-6" id="srclibrs-6"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// lifted from the `console_log` example
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(a: &str);
|
||
}
|
||
|
||
macro_rules! console_log {
|
||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||
}
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() {
|
||
let window = web_sys::window().expect("should have a window in this context");
|
||
let performance = window
|
||
.performance()
|
||
.expect("performance should be available");
|
||
|
||
console_log!("the current time (in ms) is {}", performance.now());
|
||
|
||
let start = perf_to_system(performance.timing().request_start());
|
||
let end = perf_to_system(performance.timing().response_end());
|
||
|
||
console_log!("request started at {}", humantime::format_rfc3339(start));
|
||
console_log!("request ended at {}", humantime::format_rfc3339(end));
|
||
}
|
||
|
||
fn perf_to_system(amt: f64) -> SystemTime {
|
||
let secs = (amt as u64) / 1_000;
|
||
let nanos = ((amt as u32) % 1_000) * 1_000_000;
|
||
UNIX_EPOCH + Duration::new(secs, nanos)
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#the-fetch-api" id="the-fetch-api">The <code>fetch</code> API</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/fetch">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/fetch/">view the compiled example online</a></p>
|
||
<p>This example uses the <code>fetch</code> API to make an HTTP request to the GitHub API and
|
||
then parses the resulting JSON.</p>
|
||
<h2><a class="header" href="#cargotoml-2" id="cargotoml-2"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables a number of features related to the <code>fetch</code> API and
|
||
types used: <code>Headers</code>, <code>Request</code>, etc. It also enables <code>wasm-bindgen</code>'s <code>serde</code>
|
||
support.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "fetch"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = { version = "0.2.63", features = ["serde-serialize"] }
|
||
js-sys = "0.3.40"
|
||
wasm-bindgen-futures = "0.4.13"
|
||
serde = { version = "1.0.80", features = ["derive"] }
|
||
serde_derive = "^1.0.59"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'Headers',
|
||
'Request',
|
||
'RequestInit',
|
||
'RequestMode',
|
||
'Response',
|
||
'Window',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-7" id="srclibrs-7"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use serde::{Deserialize, Serialize};
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
use wasm_bindgen_futures::JsFuture;
|
||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||
|
||
/// A struct to hold some data from the github Branch API.
|
||
///
|
||
/// Note how we don't have to define every member -- serde will ignore extra
|
||
/// data when deserializing
|
||
#[derive(Debug, Serialize, Deserialize)]
|
||
pub struct Branch {
|
||
pub name: String,
|
||
pub commit: Commit,
|
||
}
|
||
|
||
#[derive(Debug, Serialize, Deserialize)]
|
||
pub struct Commit {
|
||
pub sha: String,
|
||
pub commit: CommitDetails,
|
||
}
|
||
|
||
#[derive(Debug, Serialize, Deserialize)]
|
||
pub struct CommitDetails {
|
||
pub author: Signature,
|
||
pub committer: Signature,
|
||
}
|
||
|
||
#[derive(Debug, Serialize, Deserialize)]
|
||
pub struct Signature {
|
||
pub name: String,
|
||
pub email: String,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub async fn run(repo: String) -> Result<JsValue, JsValue> {
|
||
let mut opts = RequestInit::new();
|
||
opts.method("GET");
|
||
opts.mode(RequestMode::Cors);
|
||
|
||
let url = format!("https://api.github.com/repos/{}/branches/master", repo);
|
||
|
||
let request = Request::new_with_str_and_init(&url, &opts)?;
|
||
|
||
request
|
||
.headers()
|
||
.set("Accept", "application/vnd.github.v3+json")?;
|
||
|
||
let window = web_sys::window().unwrap();
|
||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||
|
||
// `resp_value` is a `Response` object.
|
||
assert!(resp_value.is_instance_of::<Response>());
|
||
let resp: Response = resp_value.dyn_into().unwrap();
|
||
|
||
// Convert this other `Promise` into a rust `Future`.
|
||
let json = JsFuture::from(resp.json()?).await?;
|
||
|
||
// Use serde to parse the JSON into a struct.
|
||
let branch_info: Branch = json.into_serde().unwrap();
|
||
|
||
// Send the `Branch` struct back to JS as an `Object`.
|
||
Ok(JsValue::from_serde(&branch_info).unwrap())
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#2d-canvas" id="2d-canvas">2D Canvas</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/canvas">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/canvas/">view the compiled example online</a></p>
|
||
<p>Drawing a smiley face with the 2D canvas API. This is a port of part of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes#Moving_the_pen">this
|
||
MDN
|
||
tutorial</a>
|
||
to <code>web-sys</code>.</p>
|
||
<p><img src="examples/./2d-canvas.png" alt="A smiley face" /></p>
|
||
<h2><a class="header" href="#cargotoml-3" id="cargotoml-3"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables features necessary to query the DOM and work with 2D
|
||
canvas.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "canvas"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
js-sys = "0.3.40"
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'CanvasRenderingContext2d',
|
||
'Document',
|
||
'Element',
|
||
'HtmlCanvasElement',
|
||
'Window',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-8" id="srclibrs-8"><code>src/lib.rs</code></a></h2>
|
||
<p>Gets the <code><canvas></code> element, creates a 2D rendering context, and draws the
|
||
smiley face.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use std::f64;
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn start() {
|
||
let document = web_sys::window().unwrap().document().unwrap();
|
||
let canvas = document.get_element_by_id("canvas").unwrap();
|
||
let canvas: web_sys::HtmlCanvasElement = canvas
|
||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
||
.map_err(|_| ())
|
||
.unwrap();
|
||
|
||
let context = canvas
|
||
.get_context("2d")
|
||
.unwrap()
|
||
.unwrap()
|
||
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
||
.unwrap();
|
||
|
||
context.begin_path();
|
||
|
||
// Draw the outer circle.
|
||
context
|
||
.arc(75.0, 75.0, 50.0, 0.0, f64::consts::PI * 2.0)
|
||
.unwrap();
|
||
|
||
// Draw the mouth.
|
||
context.move_to(110.0, 75.0);
|
||
context.arc(75.0, 75.0, 35.0, 0.0, f64::consts::PI).unwrap();
|
||
|
||
// Draw the left eye.
|
||
context.move_to(65.0, 65.0);
|
||
context
|
||
.arc(60.0, 65.0, 5.0, 0.0, f64::consts::PI * 2.0)
|
||
.unwrap();
|
||
|
||
// Draw the right eye.
|
||
context.move_to(95.0, 65.0);
|
||
context
|
||
.arc(90.0, 65.0, 5.0, 0.0, f64::consts::PI * 2.0)
|
||
.unwrap();
|
||
|
||
context.stroke();
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#julia-set" id="julia-set">Julia Set</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/julia_set">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/julia_set/">view the compiled example online</a></p>
|
||
<p>While not showing off a lot of <code>web_sys</code> API surface area, this example shows a
|
||
neat fractal that you can make!</p>
|
||
<h2><a class="header" href="#indexjs-2" id="indexjs-2"><code>index.js</code></a></h2>
|
||
<p>A small bit of glue is added for this example</p>
|
||
<pre><code class="language-js">import('./pkg')
|
||
.then(wasm => {
|
||
const canvas = document.getElementById('drawing');
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
const realInput = document.getElementById('real');
|
||
const imaginaryInput = document.getElementById('imaginary');
|
||
const renderBtn = document.getElementById('render');
|
||
|
||
renderBtn.addEventListener('click', () => {
|
||
const real = parseFloat(realInput.value) || 0;
|
||
const imaginary = parseFloat(imaginaryInput.value) || 0;
|
||
wasm.draw(ctx, 600, 600, real, imaginary);
|
||
});
|
||
|
||
wasm.draw(ctx, 600, 600, -0.15, 0.65);
|
||
})
|
||
.catch(console.error);
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-9" id="srclibrs-9"><code>src/lib.rs</code></a></h2>
|
||
<p>The bulk of the logic is in the generation of the fractal</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use std::ops::Add;
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::Clamped;
|
||
use web_sys::{CanvasRenderingContext2d, ImageData};
|
||
|
||
#[wasm_bindgen]
|
||
pub fn draw(
|
||
ctx: &CanvasRenderingContext2d,
|
||
width: u32,
|
||
height: u32,
|
||
real: f64,
|
||
imaginary: f64,
|
||
) -> Result<(), JsValue> {
|
||
// The real workhorse of this algorithm, generating pixel data
|
||
let c = Complex { real, imaginary };
|
||
let mut data = get_julia_set(width, height, c);
|
||
let data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut data), width, height)?;
|
||
ctx.put_image_data(&data, 0.0, 0.0)
|
||
}
|
||
|
||
fn get_julia_set(width: u32, height: u32, c: Complex) -> Vec<u8> {
|
||
let mut data = Vec::new();
|
||
|
||
let param_i = 1.5;
|
||
let param_r = 1.5;
|
||
let scale = 0.005;
|
||
|
||
for x in 0..width {
|
||
for y in 0..height {
|
||
let z = Complex {
|
||
real: y as f64 * scale - param_r,
|
||
imaginary: x as f64 * scale - param_i,
|
||
};
|
||
let iter_index = get_iter_index(z, c);
|
||
data.push((iter_index / 4) as u8);
|
||
data.push((iter_index / 2) as u8);
|
||
data.push(iter_index as u8);
|
||
data.push(255);
|
||
}
|
||
}
|
||
|
||
data
|
||
}
|
||
|
||
fn get_iter_index(z: Complex, c: Complex) -> u32 {
|
||
let mut iter_index: u32 = 0;
|
||
let mut z = z;
|
||
while iter_index < 900 {
|
||
if z.norm() > 2.0 {
|
||
break;
|
||
}
|
||
z = z.square() + c;
|
||
iter_index += 1;
|
||
}
|
||
iter_index
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug)]
|
||
struct Complex {
|
||
real: f64,
|
||
imaginary: f64,
|
||
}
|
||
|
||
impl Complex {
|
||
fn square(self) -> Complex {
|
||
let real = (self.real * self.real) - (self.imaginary * self.imaginary);
|
||
let imaginary = 2.0 * self.real * self.imaginary;
|
||
Complex { real, imaginary }
|
||
}
|
||
|
||
fn norm(&self) -> f64 {
|
||
(self.real * self.real) + (self.imaginary * self.imaginary)
|
||
}
|
||
}
|
||
|
||
impl Add<Complex> for Complex {
|
||
type Output = Complex;
|
||
|
||
fn add(self, rhs: Complex) -> Complex {
|
||
Complex {
|
||
real: self.real + rhs.real,
|
||
imaginary: self.imaginary + rhs.imaginary,
|
||
}
|
||
}
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#webaudio" id="webaudio">WebAudio</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/webaudio">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/webaudio/">view the compiled example online</a></p>
|
||
<p>This example creates an <a href="https://en.wikipedia.org/wiki/Frequency_modulation_synthesis">FM
|
||
oscillator</a> using
|
||
the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API">WebAudio
|
||
API</a> and
|
||
<code>web-sys</code>.</p>
|
||
<h2><a class="header" href="#cargotoml-4" id="cargotoml-4"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables the types needed to use the relevant bits of the
|
||
WebAudio API.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "webaudio"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'AudioContext',
|
||
'AudioDestinationNode',
|
||
'AudioNode',
|
||
'AudioParam',
|
||
'GainNode',
|
||
'OscillatorNode',
|
||
'OscillatorType',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-10" id="srclibrs-10"><code>src/lib.rs</code></a></h2>
|
||
<p>The Rust code implements the FM oscillator.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
use web_sys::{AudioContext, OscillatorType};
|
||
|
||
/// Converts a midi note to frequency
|
||
///
|
||
/// A midi note is an integer, generally in the range of 21 to 108
|
||
pub fn midi_to_freq(note: u8) -> f32 {
|
||
27.5 * 2f32.powf((note as f32 - 21.0) / 12.0)
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub struct FmOsc {
|
||
ctx: AudioContext,
|
||
/// The primary oscillator. This will be the fundamental frequency
|
||
primary: web_sys::OscillatorNode,
|
||
|
||
/// Overall gain (volume) control
|
||
gain: web_sys::GainNode,
|
||
|
||
/// Amount of frequency modulation
|
||
fm_gain: web_sys::GainNode,
|
||
|
||
/// The oscillator that will modulate the primary oscillator's frequency
|
||
fm_osc: web_sys::OscillatorNode,
|
||
|
||
/// The ratio between the primary frequency and the fm_osc frequency.
|
||
///
|
||
/// Generally fractional values like 1/2 or 1/4 sound best
|
||
fm_freq_ratio: f32,
|
||
|
||
fm_gain_ratio: f32,
|
||
}
|
||
|
||
impl Drop for FmOsc {
|
||
fn drop(&mut self) {
|
||
let _ = self.ctx.close();
|
||
}
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl FmOsc {
|
||
#[wasm_bindgen(constructor)]
|
||
pub fn new() -> Result<FmOsc, JsValue> {
|
||
let ctx = web_sys::AudioContext::new()?;
|
||
|
||
// Create our web audio objects.
|
||
let primary = ctx.create_oscillator()?;
|
||
let fm_osc = ctx.create_oscillator()?;
|
||
let gain = ctx.create_gain()?;
|
||
let fm_gain = ctx.create_gain()?;
|
||
|
||
// Some initial settings:
|
||
primary.set_type(OscillatorType::Sine);
|
||
primary.frequency().set_value(440.0); // A4 note
|
||
gain.gain().set_value(0.0); // starts muted
|
||
fm_gain.gain().set_value(0.0); // no initial frequency modulation
|
||
fm_osc.set_type(OscillatorType::Sine);
|
||
fm_osc.frequency().set_value(0.0);
|
||
|
||
// Connect the nodes up!
|
||
|
||
// The primary oscillator is routed through the gain node, so that
|
||
// it can control the overall output volume.
|
||
primary.connect_with_audio_node(&gain)?;
|
||
|
||
// Then connect the gain node to the AudioContext destination (aka
|
||
// your speakers).
|
||
gain.connect_with_audio_node(&ctx.destination())?;
|
||
|
||
// The FM oscillator is connected to its own gain node, so it can
|
||
// control the amount of modulation.
|
||
fm_osc.connect_with_audio_node(&fm_gain)?;
|
||
|
||
// Connect the FM oscillator to the frequency parameter of the main
|
||
// oscillator, so that the FM node can modulate its frequency.
|
||
fm_gain.connect_with_audio_param(&primary.frequency())?;
|
||
|
||
// Start the oscillators!
|
||
primary.start()?;
|
||
fm_osc.start()?;
|
||
|
||
Ok(FmOsc {
|
||
ctx,
|
||
primary,
|
||
gain,
|
||
fm_gain,
|
||
fm_osc,
|
||
fm_freq_ratio: 0.0,
|
||
fm_gain_ratio: 0.0,
|
||
})
|
||
}
|
||
|
||
/// Sets the gain for this oscillator, between 0.0 and 1.0.
|
||
#[wasm_bindgen]
|
||
pub fn set_gain(&self, mut gain: f32) {
|
||
if gain > 1.0 {
|
||
gain = 1.0;
|
||
}
|
||
if gain < 0.0 {
|
||
gain = 0.0;
|
||
}
|
||
self.gain.gain().set_value(gain);
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn set_primary_frequency(&self, freq: f32) {
|
||
self.primary.frequency().set_value(freq);
|
||
|
||
// The frequency of the FM oscillator depends on the frequency of the
|
||
// primary oscillator, so we update the frequency of both in this method.
|
||
self.fm_osc.frequency().set_value(self.fm_freq_ratio * freq);
|
||
self.fm_gain.gain().set_value(self.fm_gain_ratio * freq);
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn set_note(&self, note: u8) {
|
||
let freq = midi_to_freq(note);
|
||
self.set_primary_frequency(freq);
|
||
}
|
||
|
||
/// This should be between 0 and 1, though higher values are accepted.
|
||
#[wasm_bindgen]
|
||
pub fn set_fm_amount(&mut self, amt: f32) {
|
||
self.fm_gain_ratio = amt;
|
||
|
||
self.fm_gain
|
||
.gain()
|
||
.set_value(self.fm_gain_ratio * self.primary.frequency().value());
|
||
}
|
||
|
||
/// This should be between 0 and 1, though higher values are accepted.
|
||
#[wasm_bindgen]
|
||
pub fn set_fm_frequency(&mut self, amt: f32) {
|
||
self.fm_freq_ratio = amt;
|
||
self.fm_osc
|
||
.frequency()
|
||
.set_value(self.fm_freq_ratio * self.primary.frequency().value());
|
||
}
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#indexjs-3" id="indexjs-3"><code>index.js</code></a></h2>
|
||
<p>A small bit of JavaScript glues the rust module to input widgets and translates
|
||
events into calls into wasm code.</p>
|
||
<pre><code class="language-js">import('./pkg')
|
||
.then(rust_module => {
|
||
let fm = null;
|
||
|
||
const play_button = document.getElementById("play");
|
||
play_button.addEventListener("click", event => {
|
||
if (fm === null) {
|
||
fm = new rust_module.FmOsc();
|
||
fm.set_note(50);
|
||
fm.set_fm_frequency(0);
|
||
fm.set_fm_amount(0);
|
||
fm.set_gain(0.8);
|
||
} else {
|
||
fm.free();
|
||
fm = null;
|
||
}
|
||
});
|
||
|
||
const primary_slider = document.getElementById("primary_input");
|
||
primary_slider.addEventListener("input", event => {
|
||
if (fm) {
|
||
fm.set_note(parseInt(event.target.value));
|
||
}
|
||
});
|
||
|
||
const fm_freq = document.getElementById("fm_freq");
|
||
fm_freq.addEventListener("input", event => {
|
||
if (fm) {
|
||
fm.set_fm_frequency(parseFloat(event.target.value));
|
||
}
|
||
});
|
||
|
||
const fm_amount = document.getElementById("fm_amount");
|
||
fm_amount.addEventListener("input", event => {
|
||
if (fm) {
|
||
fm.set_fm_amount(parseFloat(event.target.value));
|
||
}
|
||
});
|
||
})
|
||
.catch(console.error);
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#webgl-example" id="webgl-example">WebGL Example</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/webgl">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/webgl/">view the compiled example online</a></p>
|
||
<p>This example draws a triangle to the screen using the WebGL API.</p>
|
||
<h2><a class="header" href="#cargotoml-5" id="cargotoml-5"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables features necessary to obtain and use a WebGL
|
||
rendering context.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "webgl"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
js-sys = "0.3.40"
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'Document',
|
||
'Element',
|
||
'HtmlCanvasElement',
|
||
'WebGlBuffer',
|
||
'WebGlRenderingContext',
|
||
'WebGlProgram',
|
||
'WebGlShader',
|
||
'Window',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-11" id="srclibrs-11"><code>src/lib.rs</code></a></h2>
|
||
<p>This source file handles all of the necessary logic to obtain a rendering
|
||
context, compile shaders, fill a buffer with vertex coordinates, and draw a
|
||
triangle to the screen.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
use web_sys::{WebGlProgram, WebGlRenderingContext, WebGlShader};
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn start() -> Result<(), JsValue> {
|
||
let document = web_sys::window().unwrap().document().unwrap();
|
||
let canvas = document.get_element_by_id("canvas").unwrap();
|
||
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||
|
||
let context = canvas
|
||
.get_context("webgl")?
|
||
.unwrap()
|
||
.dyn_into::<WebGlRenderingContext>()?;
|
||
|
||
let vert_shader = compile_shader(
|
||
&context,
|
||
WebGlRenderingContext::VERTEX_SHADER,
|
||
r#"
|
||
attribute vec4 position;
|
||
void main() {
|
||
gl_Position = position;
|
||
}
|
||
"#,
|
||
)?;
|
||
let frag_shader = compile_shader(
|
||
&context,
|
||
WebGlRenderingContext::FRAGMENT_SHADER,
|
||
r#"
|
||
void main() {
|
||
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||
}
|
||
"#,
|
||
)?;
|
||
let program = link_program(&context, &vert_shader, &frag_shader)?;
|
||
context.use_program(Some(&program));
|
||
|
||
let vertices: [f32; 9] = [-0.7, -0.7, 0.0, 0.7, -0.7, 0.0, 0.0, 0.7, 0.0];
|
||
|
||
let buffer = context.create_buffer().ok_or("failed to create buffer")?;
|
||
context.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&buffer));
|
||
|
||
// Note that `Float32Array::view` is somewhat dangerous (hence the
|
||
// `unsafe`!). This is creating a raw view into our module's
|
||
// `WebAssembly.Memory` buffer, but if we allocate more pages for ourself
|
||
// (aka do a memory allocation in Rust) it'll cause the buffer to change,
|
||
// causing the `Float32Array` to be invalid.
|
||
//
|
||
// As a result, after `Float32Array::view` we have to be very careful not to
|
||
// do any memory allocations before it's dropped.
|
||
unsafe {
|
||
let vert_array = js_sys::Float32Array::view(&vertices);
|
||
|
||
context.buffer_data_with_array_buffer_view(
|
||
WebGlRenderingContext::ARRAY_BUFFER,
|
||
&vert_array,
|
||
WebGlRenderingContext::STATIC_DRAW,
|
||
);
|
||
}
|
||
|
||
context.vertex_attrib_pointer_with_i32(0, 3, WebGlRenderingContext::FLOAT, false, 0, 0);
|
||
context.enable_vertex_attrib_array(0);
|
||
|
||
context.clear_color(0.0, 0.0, 0.0, 1.0);
|
||
context.clear(WebGlRenderingContext::COLOR_BUFFER_BIT);
|
||
|
||
context.draw_arrays(
|
||
WebGlRenderingContext::TRIANGLES,
|
||
0,
|
||
(vertices.len() / 3) as i32,
|
||
);
|
||
Ok(())
|
||
}
|
||
|
||
pub fn compile_shader(
|
||
context: &WebGlRenderingContext,
|
||
shader_type: u32,
|
||
source: &str,
|
||
) -> Result<WebGlShader, String> {
|
||
let shader = context
|
||
.create_shader(shader_type)
|
||
.ok_or_else(|| String::from("Unable to create shader object"))?;
|
||
context.shader_source(&shader, source);
|
||
context.compile_shader(&shader);
|
||
|
||
if context
|
||
.get_shader_parameter(&shader, WebGlRenderingContext::COMPILE_STATUS)
|
||
.as_bool()
|
||
.unwrap_or(false)
|
||
{
|
||
Ok(shader)
|
||
} else {
|
||
Err(context
|
||
.get_shader_info_log(&shader)
|
||
.unwrap_or_else(|| String::from("Unknown error creating shader")))
|
||
}
|
||
}
|
||
|
||
pub fn link_program(
|
||
context: &WebGlRenderingContext,
|
||
vert_shader: &WebGlShader,
|
||
frag_shader: &WebGlShader,
|
||
) -> Result<WebGlProgram, String> {
|
||
let program = context
|
||
.create_program()
|
||
.ok_or_else(|| String::from("Unable to create shader object"))?;
|
||
|
||
context.attach_shader(&program, vert_shader);
|
||
context.attach_shader(&program, frag_shader);
|
||
context.link_program(&program);
|
||
|
||
if context
|
||
.get_program_parameter(&program, WebGlRenderingContext::LINK_STATUS)
|
||
.as_bool()
|
||
.unwrap_or(false)
|
||
{
|
||
Ok(program)
|
||
} else {
|
||
Err(context
|
||
.get_program_info_log(&program)
|
||
.unwrap_or_else(|| String::from("Unknown error creating program object")))
|
||
}
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#websockets-example" id="websockets-example">WebSockets Example</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/websockets/">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/websockets/">view the compiled example online</a></p>
|
||
<p>This example connects to an echo server on <code>wss://echo.websocket.org</code>,
|
||
sends a <code>ping</code> message, and receives the response.</p>
|
||
<h2><a class="header" href="#cargotoml-6" id="cargotoml-6"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables features necessary to create a <code>WebSocket</code> object and
|
||
to access events such as <code>MessageEvent</code> or <code>ErrorEvent</code>.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "websockets"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = "0.2.63"
|
||
js-sys = "0.3"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.22"
|
||
features = [
|
||
"BinaryType",
|
||
"Blob",
|
||
"ErrorEvent",
|
||
"FileReader",
|
||
"MessageEvent",
|
||
"ProgressEvent",
|
||
"WebSocket",
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-12" id="srclibrs-12"><code>src/lib.rs</code></a></h2>
|
||
<p>This code shows the basic steps required to work with a <code>WebSocket</code>.
|
||
At first it opens the connection, then subscribes to events <code>onmessage</code>, <code>onerror</code>, <code>onopen</code>.
|
||
After the socket is opened it sends a <code>ping</code> message, receives an echoed response
|
||
and prints it to the browser console.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
use web_sys::{ErrorEvent, MessageEvent, WebSocket};
|
||
|
||
macro_rules! console_log {
|
||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
}
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn start_websocket() -> Result<(), JsValue> {
|
||
// Connect to an echo server
|
||
let ws = WebSocket::new("wss://echo.websocket.org")?;
|
||
// For small binary messages, like CBOR, Arraybuffer is more efficient than Blob handling
|
||
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
|
||
// create callback
|
||
let cloned_ws = ws.clone();
|
||
let onmessage_callback = Closure::wrap(Box::new(move |e: MessageEvent| {
|
||
// Handle difference Text/Binary,...
|
||
if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
|
||
console_log!("message event, received arraybuffer: {:?}", abuf);
|
||
let array = js_sys::Uint8Array::new(&abuf);
|
||
let len = array.byte_length() as usize;
|
||
console_log!("Arraybuffer received {}bytes: {:?}", len, array.to_vec());
|
||
// here you can for example use Serde Deserialize decode the message
|
||
// for demo purposes we switch back to Blob-type and send off another binary message
|
||
cloned_ws.set_binary_type(web_sys::BinaryType::Blob);
|
||
match cloned_ws.send_with_u8_array(&vec![5, 6, 7, 8]) {
|
||
Ok(_) => console_log!("binary message successfully sent"),
|
||
Err(err) => console_log!("error sending message: {:?}", err),
|
||
}
|
||
} else if let Ok(blob) = e.data().dyn_into::<web_sys::Blob>() {
|
||
console_log!("message event, received blob: {:?}", blob);
|
||
// better alternative to juggling with FileReader is to use https://crates.io/crates/gloo-file
|
||
let fr = web_sys::FileReader::new().unwrap();
|
||
let fr_c = fr.clone();
|
||
// create onLoadEnd callback
|
||
let onloadend_cb = Closure::wrap(Box::new(move |_e: web_sys::ProgressEvent| {
|
||
let array = js_sys::Uint8Array::new(&fr_c.result().unwrap());
|
||
let len = array.byte_length() as usize;
|
||
console_log!("Blob received {}bytes: {:?}", len, array.to_vec());
|
||
// here you can for example use the received image/png data
|
||
})
|
||
as Box<dyn FnMut(web_sys::ProgressEvent)>);
|
||
fr.set_onloadend(Some(onloadend_cb.as_ref().unchecked_ref()));
|
||
fr.read_as_array_buffer(&blob).expect("blob not readable");
|
||
onloadend_cb.forget();
|
||
} else if let Ok(txt) = e.data().dyn_into::<js_sys::JsString>() {
|
||
console_log!("message event, received Text: {:?}", txt);
|
||
} else {
|
||
console_log!("message event, received Unknown: {:?}", e.data());
|
||
}
|
||
}) as Box<dyn FnMut(MessageEvent)>);
|
||
// set message event handler on WebSocket
|
||
ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||
// forget the callback to keep it alive
|
||
onmessage_callback.forget();
|
||
|
||
let onerror_callback = Closure::wrap(Box::new(move |e: ErrorEvent| {
|
||
console_log!("error event: {:?}", e);
|
||
}) as Box<dyn FnMut(ErrorEvent)>);
|
||
ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
|
||
onerror_callback.forget();
|
||
|
||
let cloned_ws = ws.clone();
|
||
let onopen_callback = Closure::wrap(Box::new(move |_| {
|
||
console_log!("socket opened");
|
||
match cloned_ws.send_with_str("ping") {
|
||
Ok(_) => console_log!("message successfully sent"),
|
||
Err(err) => console_log!("error sending message: {:?}", err),
|
||
}
|
||
// send off binary message
|
||
match cloned_ws.send_with_u8_array(&vec![0, 1, 2, 3]) {
|
||
Ok(_) => console_log!("binary message successfully sent"),
|
||
Err(err) => console_log!("error sending message: {:?}", err),
|
||
}
|
||
}) as Box<dyn FnMut(JsValue)>);
|
||
ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
|
||
onopen_callback.forget();
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#webrtc-datachannel-example" id="webrtc-datachannel-example">WebRTC DataChannel Example</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/webrtc_datachannel/">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/webrtc_datachannel/">view the compiled example online</a></p>
|
||
<p>This example creates 2 peer connections and 2 data channels in single browser tab.
|
||
Send ping/pong between <code>peer1.dc</code> and <code>peer2.dc</code>.</p>
|
||
<h2><a class="header" href="#cargotoml-7" id="cargotoml-7"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables features necessary to use WebRTC DataChannel and its negotiation.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "webrtc_datachannel"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = "0.2.63"
|
||
js-sys = "0.3"
|
||
wasm-bindgen-futures = "0.4.13"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.22"
|
||
features = [
|
||
"MessageEvent",
|
||
"RtcPeerConnection",
|
||
"RtcSignalingState",
|
||
"RtcSdpType",
|
||
"RtcSessionDescriptionInit",
|
||
"RtcPeerConnectionIceEvent",
|
||
"RtcIceCandidate",
|
||
"RtcDataChannel",
|
||
"RtcDataChannelEvent",
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-13" id="srclibrs-13"><code>src/lib.rs</code></a></h2>
|
||
<p>The Rust code connects WebRTC data channel.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use js_sys::Reflect;
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
use wasm_bindgen_futures::JsFuture;
|
||
use web_sys::{
|
||
MessageEvent, RtcDataChannelEvent, RtcPeerConnection, RtcPeerConnectionIceEvent, RtcSdpType,
|
||
RtcSessionDescriptionInit,
|
||
};
|
||
|
||
macro_rules! console_log {
|
||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||
}
|
||
macro_rules! console_warn {
|
||
($($t:tt)*) => (warn(&format_args!($($t)*).to_string()))
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn warn(s: &str);
|
||
}
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub async fn start() -> Result<(), JsValue> {
|
||
/*
|
||
* Set up PeerConnections
|
||
* pc1 <=> pc2
|
||
*
|
||
*/
|
||
let pc1 = RtcPeerConnection::new()?;
|
||
console_log!("pc1 created: state {:?}", pc1.signaling_state());
|
||
let pc2 = RtcPeerConnection::new()?;
|
||
console_log!("pc2 created: state {:?}", pc2.signaling_state());
|
||
|
||
/*
|
||
* Create DataChannel on pc1 to negotiate
|
||
* Message will be shonw here after connection established
|
||
*
|
||
*/
|
||
let dc1 = pc1.create_data_channel("my-data-channel");
|
||
console_log!("dc1 created: label {:?}", dc1.label());
|
||
|
||
let dc1_clone = dc1.clone();
|
||
let onmessage_callback =
|
||
Closure::wrap(
|
||
Box::new(move |ev: MessageEvent| match ev.data().as_string() {
|
||
Some(message) => {
|
||
console_warn!("{:?}", message);
|
||
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
|
||
}
|
||
None => {}
|
||
}) as Box<dyn FnMut(MessageEvent)>,
|
||
);
|
||
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||
onmessage_callback.forget();
|
||
|
||
/*
|
||
* If negotiaion has done, this closure will be called
|
||
*
|
||
*/
|
||
let ondatachannel_callback = Closure::wrap(Box::new(move |ev: RtcDataChannelEvent| {
|
||
let dc2 = ev.channel();
|
||
console_log!("pc2.ondatachannel!: {:?}", dc2.label());
|
||
|
||
let onmessage_callback =
|
||
Closure::wrap(
|
||
Box::new(move |ev: MessageEvent| match ev.data().as_string() {
|
||
Some(message) => console_warn!("{:?}", message),
|
||
None => {}
|
||
}) as Box<dyn FnMut(MessageEvent)>,
|
||
);
|
||
dc2.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||
onmessage_callback.forget();
|
||
|
||
dc2.send_with_str("Ping from pc2.dc!").unwrap();
|
||
}) as Box<dyn FnMut(RtcDataChannelEvent)>);
|
||
pc2.set_ondatachannel(Some(ondatachannel_callback.as_ref().unchecked_ref()));
|
||
ondatachannel_callback.forget();
|
||
|
||
/*
|
||
* Handle ICE candidate each other
|
||
*
|
||
*/
|
||
let pc2_clone = pc2.clone();
|
||
let onicecandidate_callback1 =
|
||
Closure::wrap(
|
||
Box::new(move |ev: RtcPeerConnectionIceEvent| match ev.candidate() {
|
||
Some(candidate) => {
|
||
console_log!("pc1.onicecandidate: {:#?}", candidate.candidate());
|
||
let _ =
|
||
pc2_clone.add_ice_candidate_with_opt_rtc_ice_candidate(Some(&candidate));
|
||
}
|
||
None => {}
|
||
}) as Box<dyn FnMut(RtcPeerConnectionIceEvent)>,
|
||
);
|
||
pc1.set_onicecandidate(Some(onicecandidate_callback1.as_ref().unchecked_ref()));
|
||
onicecandidate_callback1.forget();
|
||
|
||
let pc1_clone = pc1.clone();
|
||
let onicecandidate_callback2 =
|
||
Closure::wrap(
|
||
Box::new(move |ev: RtcPeerConnectionIceEvent| match ev.candidate() {
|
||
Some(candidate) => {
|
||
console_log!("pc2.onicecandidate: {:#?}", candidate.candidate());
|
||
let _ =
|
||
pc1_clone.add_ice_candidate_with_opt_rtc_ice_candidate(Some(&candidate));
|
||
}
|
||
None => {}
|
||
}) as Box<dyn FnMut(RtcPeerConnectionIceEvent)>,
|
||
);
|
||
pc2.set_onicecandidate(Some(onicecandidate_callback2.as_ref().unchecked_ref()));
|
||
onicecandidate_callback2.forget();
|
||
|
||
/*
|
||
* Send OFFER from pc1 to pc2
|
||
*
|
||
*/
|
||
let offer = JsFuture::from(pc1.create_offer()).await?;
|
||
let offer_sdp = Reflect::get(&offer, &JsValue::from_str("sdp"))?
|
||
.as_string()
|
||
.unwrap();
|
||
console_log!("pc1: offer {:?}", offer_sdp);
|
||
|
||
let mut offer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
||
offer_obj.sdp(&offer_sdp);
|
||
let sld_promise = pc1.set_local_description(&offer_obj);
|
||
JsFuture::from(sld_promise).await?;
|
||
console_log!("pc1: state {:?}", pc1.signaling_state());
|
||
|
||
/*
|
||
* Receive OFFER from pc1
|
||
* Create and send ANSWER from pc2 to pc1
|
||
*
|
||
*/
|
||
let mut offer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Offer);
|
||
offer_obj.sdp(&offer_sdp);
|
||
let srd_promise = pc2.set_remote_description(&offer_obj);
|
||
JsFuture::from(srd_promise).await?;
|
||
console_log!("pc2: state {:?}", pc2.signaling_state());
|
||
|
||
let answer = JsFuture::from(pc2.create_answer()).await?;
|
||
let answer_sdp = Reflect::get(&answer, &JsValue::from_str("sdp"))?
|
||
.as_string()
|
||
.unwrap();
|
||
console_log!("pc2: answer {:?}", answer_sdp);
|
||
|
||
let mut answer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
||
answer_obj.sdp(&answer_sdp);
|
||
let sld_promise = pc2.set_local_description(&answer_obj);
|
||
JsFuture::from(sld_promise).await?;
|
||
console_log!("pc2: state {:?}", pc2.signaling_state());
|
||
|
||
/*
|
||
* Receive ANSWER from pc2
|
||
*
|
||
*/
|
||
let mut answer_obj = RtcSessionDescriptionInit::new(RtcSdpType::Answer);
|
||
answer_obj.sdp(&answer_sdp);
|
||
let srd_promise = pc1.set_remote_description(&answer_obj);
|
||
JsFuture::from(srd_promise).await?;
|
||
console_log!("pc1: state {:?}", pc1.signaling_state());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#web-sys-a-requestanimationframe-loop" id="web-sys-a-requestanimationframe-loop"><code>web-sys</code>: A <code>requestAnimationFrame</code> Loop</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/request-animation-frame">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/request-animation-frame/">view the compiled example online</a></p>
|
||
<p>This is an example of a <code>requestAnimationFrame</code> loop using the <code>web-sys</code> crate!
|
||
It renders a count of how many times a <code>requestAnimationFrame</code> callback has been
|
||
invoked and then it breaks out of the <code>requestAnimationFrame</code> loop after 300
|
||
iterations.</p>
|
||
<h2><a class="header" href="#cargotoml-8" id="cargotoml-8"><code>Cargo.toml</code></a></h2>
|
||
<p>You can see here how we depend on <code>web-sys</code> and activate associated features to
|
||
enable all the various APIs:</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "request-animation-frame"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'Document',
|
||
'Element',
|
||
'HtmlElement',
|
||
'Node',
|
||
'Window',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-14" id="srclibrs-14"><code>src/lib.rs</code></a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use std::cell::RefCell;
|
||
use std::rc::Rc;
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
|
||
fn window() -> web_sys::Window {
|
||
web_sys::window().expect("no global `window` exists")
|
||
}
|
||
|
||
fn request_animation_frame(f: &Closure<dyn FnMut()>) {
|
||
window()
|
||
.request_animation_frame(f.as_ref().unchecked_ref())
|
||
.expect("should register `requestAnimationFrame` OK");
|
||
}
|
||
|
||
fn document() -> web_sys::Document {
|
||
window()
|
||
.document()
|
||
.expect("should have a document on window")
|
||
}
|
||
|
||
fn body() -> web_sys::HtmlElement {
|
||
document().body().expect("document should have a body")
|
||
}
|
||
|
||
// This function is automatically invoked after the wasm module is instantiated.
|
||
#[wasm_bindgen(start)]
|
||
pub fn run() -> Result<(), JsValue> {
|
||
// Here we want to call `requestAnimationFrame` in a loop, but only a fixed
|
||
// number of times. After it's done we want all our resources cleaned up. To
|
||
// achieve this we're using an `Rc`. The `Rc` will eventually store the
|
||
// closure we want to execute on each frame, but to start out it contains
|
||
// `None`.
|
||
//
|
||
// After the `Rc` is made we'll actually create the closure, and the closure
|
||
// will reference one of the `Rc` instances. The other `Rc` reference is
|
||
// used to store the closure, request the first frame, and then is dropped
|
||
// by this function.
|
||
//
|
||
// Inside the closure we've got a persistent `Rc` reference, which we use
|
||
// for all future iterations of the loop
|
||
let f = Rc::new(RefCell::new(None));
|
||
let g = f.clone();
|
||
|
||
let mut i = 0;
|
||
*g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
|
||
if i > 300 {
|
||
body().set_text_content(Some("All done!"));
|
||
|
||
// Drop our handle to this closure so that it will get cleaned
|
||
// up once we return.
|
||
let _ = f.borrow_mut().take();
|
||
return;
|
||
}
|
||
|
||
// Set the body's text content to how many times this
|
||
// requestAnimationFrame callback has fired.
|
||
i += 1;
|
||
let text = format!("requestAnimationFrame has been called {} times.", i);
|
||
body().set_text_content(Some(&text));
|
||
|
||
// Schedule ourself for another requestAnimationFrame callback.
|
||
request_animation_frame(f.borrow().as_ref().unwrap());
|
||
}) as Box<dyn FnMut()>));
|
||
|
||
request_animation_frame(g.borrow().as_ref().unwrap());
|
||
Ok(())
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#paint-example" id="paint-example">Paint Example</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/paint">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/paint/">view the compiled example online</a></p>
|
||
<p>A simple painting program.</p>
|
||
<h2><a class="header" href="#cargotoml-9" id="cargotoml-9"><code>Cargo.toml</code></a></h2>
|
||
<p>The <code>Cargo.toml</code> enables features necessary to work with the DOM, events and
|
||
2D canvas.</p>
|
||
<pre><code class="language-toml">[package]
|
||
name = "wasm-bindgen-paint"
|
||
version = "0.1.0"
|
||
authors = ["The wasm-bindgen Developers"]
|
||
edition = "2018"
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
js-sys = "0.3.40"
|
||
wasm-bindgen = "0.2.63"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3.4"
|
||
features = [
|
||
'CanvasRenderingContext2d',
|
||
'CssStyleDeclaration',
|
||
'Document',
|
||
'Element',
|
||
'EventTarget',
|
||
'HtmlCanvasElement',
|
||
'HtmlElement',
|
||
'MouseEvent',
|
||
'Node',
|
||
'Window',
|
||
]
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#srclibrs-15" id="srclibrs-15"><code>src/lib.rs</code></a></h2>
|
||
<p>Creates the <code><canvas></code> element, applies a CSS style to it, adds it to the document,
|
||
get a 2D rendering context and adds listeners for mouse events.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use std::cell::Cell;
|
||
use std::rc::Rc;
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen::JsCast;
|
||
|
||
#[wasm_bindgen(start)]
|
||
pub fn start() -> Result<(), JsValue> {
|
||
let document = web_sys::window().unwrap().document().unwrap();
|
||
let canvas = document
|
||
.create_element("canvas")?
|
||
.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||
document.body().unwrap().append_child(&canvas)?;
|
||
canvas.set_width(640);
|
||
canvas.set_height(480);
|
||
canvas.style().set_property("border", "solid")?;
|
||
let context = canvas
|
||
.get_context("2d")?
|
||
.unwrap()
|
||
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
|
||
let context = Rc::new(context);
|
||
let pressed = Rc::new(Cell::new(false));
|
||
{
|
||
let context = context.clone();
|
||
let pressed = pressed.clone();
|
||
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
||
context.begin_path();
|
||
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
||
pressed.set(true);
|
||
}) as Box<dyn FnMut(_)>);
|
||
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())?;
|
||
closure.forget();
|
||
}
|
||
{
|
||
let context = context.clone();
|
||
let pressed = pressed.clone();
|
||
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
||
if pressed.get() {
|
||
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
|
||
context.stroke();
|
||
context.begin_path();
|
||
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
||
}
|
||
}) as Box<dyn FnMut(_)>);
|
||
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())?;
|
||
closure.forget();
|
||
}
|
||
{
|
||
let context = context.clone();
|
||
let pressed = pressed.clone();
|
||
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
||
pressed.set(false);
|
||
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
|
||
context.stroke();
|
||
}) as Box<dyn FnMut(_)>);
|
||
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())?;
|
||
closure.forget();
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#parallel-raytracing" id="parallel-raytracing">Parallel Raytracing</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/raytrace-parallel">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/raytrace-parallel/">view the compiled example online</a></p>
|
||
<p><strong>This is an unstable and experimental example</strong> of using threads with
|
||
WebAssembly and Rust, culminating in a parallel raytracer demo. The browser requirements are:</p>
|
||
<ul>
|
||
<li>Firefox Nightly
|
||
<ul>
|
||
<li><code>SharedArrayBuffer</code> is enabled in <code>about:config</code> in Firefox</li>
|
||
</ul>
|
||
</li>
|
||
<li>Google Chrome
|
||
<ul>
|
||
<li>No flags required on recent versions of Chrome</li>
|
||
</ul>
|
||
</li>
|
||
<li>other browsers haven't implemented the proposed WebAssembly features yet.</li>
|
||
</ul>
|
||
<p>Locally to build this demo you'll need <code>xargo</code> and the <code>rust-src</code> rustup
|
||
component, and afterwards <code>./build.sh</code> like other examples should build the
|
||
example.</p>
|
||
<p>Again, to reiterate, this is all experimental and we're working through various
|
||
issues as we're working on this. If you're curious to see how this works it's
|
||
best to explore via the source code right now! More info will be available here
|
||
once WebAssembly threads are closer to stabilization.</p>
|
||
<h1><a class="header" href="#todo-mvc-using-wasm-bingen-and-web-sys" id="todo-mvc-using-wasm-bingen-and-web-sys">TODO MVC using wasm-bingen and web-sys</a></h1>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/todomvc">View full source code</a> or <a href="https://rustwasm.github.io/wasm-bindgen/exbuild/todomvc/">view the compiled example online</a></p>
|
||
<p><a href="https://github.com/rustwasm/wasm-bindgen">wasm-bindgen</a> and <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/">web-sys</a> coded <a href="http://todomvc.com/">TODO MVC</a></p>
|
||
<p>The code was rewritten from the <a href="http://todomvc.com/examples/vanilla-es6/">ES6 version</a>.</p>
|
||
<p>The core differences are:</p>
|
||
<ul>
|
||
<li>Having an <a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/todomvc/src/element.rs">Element wrapper</a> that takes care of dyn and into refs in web-sys,</li>
|
||
<li>A <a href="https://github.com/rustwasm/wasm-bindgen/tree/master/examples/todomvc/src/scheduler.rs">Scheduler</a> that allows Controller and View to communicate to each other by emulating something similar to the JS event loop.</li>
|
||
</ul>
|
||
<h2><a class="header" href="#size" id="size">Size</a></h2>
|
||
<p>The size of the project hasn't undergone much work to make it optimised yet.</p>
|
||
<ul>
|
||
<li>~96kb release build</li>
|
||
<li>~76kb optimised with binaryen</li>
|
||
<li>~28kb brotli compressed</li>
|
||
</ul>
|
||
<h1><a class="header" href="#reference" id="reference">Reference</a></h1>
|
||
<p>This section contains reference material for using <code>wasm-bindgen</code>. It is not
|
||
intended to be read start to finish. Instead, it aims to quickly answer
|
||
questions like:</p>
|
||
<ul>
|
||
<li>
|
||
<p>Is type X supported as a parameter in a Rust function exported to JavaScript?</p>
|
||
</li>
|
||
<li>
|
||
<p>What was that CLI flag to disable ECMAScript modules output, and instead
|
||
attach the JavaScript bindings directly to <code>window</code>?</p>
|
||
</li>
|
||
</ul>
|
||
<h1><a class="header" href="#deploying-rust-and-webassembly" id="deploying-rust-and-webassembly">Deploying Rust and WebAssembly</a></h1>
|
||
<p>At this point in time deploying Rust and WebAssembly to the web or other
|
||
locations unfortunately isn't a trivial task to do. This page hopes to serve
|
||
as documentation for the various known options, and as always PRs are welcome
|
||
to update this if it's out of date!</p>
|
||
<p>The methods of deployment and integration here are primarily tied to the
|
||
<code>--target</code> flag. Note that the <code>--target</code> flag of <code>wasm-pack</code> and <code>wasm-bindgen</code>
|
||
should behave the same way in this respect. The values possible here are:</p>
|
||
<table><thead><tr><th>Value</th><th>Summary</th></tr></thead><tbody>
|
||
<tr><td><a href="reference/#bundlers"><code>bundler</code></a></td><td>Suitable for loading in bundlers like Webpack</td></tr>
|
||
<tr><td><a href="reference/#without-a-bundler"><code>web</code></a></td><td>Directly loadable in a web browser</td></tr>
|
||
<tr><td><a href="reference/#nodejs"><code>nodejs</code></a></td><td>Loadable via <code>require</code> as a Node.js module</td></tr>
|
||
<tr><td><a href="reference/#Deno"><code>deno</code></a></td><td>Loadable using imports from Deno modules</td></tr>
|
||
<tr><td><a href="reference/#without-a-bundler"><code>no-modules</code></a></td><td>Like <code>web</code>, but older and doesn't use ES modules</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#bundlers" id="bundlers">Bundlers</a></h2>
|
||
<p><strong><code>--target bundler</code></strong></p>
|
||
<p>The default output of <code>wasm-bindgen</code>, or the <code>bundler</code> target, assumes a model
|
||
where the wasm module itself is natively an ES module. This model, however, is not
|
||
natively implemented in any JS implementation at this time. As a result, to
|
||
consume the default output of <code>wasm-bindgen</code> you will need a bundler of some
|
||
form.</p>
|
||
<blockquote>
|
||
<p><strong>Note</strong>: the choice of this default output was done to reflect the trends of
|
||
the JS ecosystem. While tools other than bundlers don't support wasm files as
|
||
native ES modules today they're all very much likely to in the future!</p>
|
||
</blockquote>
|
||
<p>Currently the only known bundler known to be fully compatible with
|
||
<code>wasm-bindgen</code> is <a href="https://webpack.js.org/">webpack</a>. Most <a href="reference/../examples/index.html">examples</a> use webpack, and you can check out
|
||
the <a href="reference/../examples/hello-world.html">hello world example online</a> to see the details of webpack configuration
|
||
necessary.</p>
|
||
<h2><a class="header" href="#without-a-bundler-1" id="without-a-bundler-1">Without a Bundler</a></h2>
|
||
<p><strong><code>--target web</code> or <code>--target no-modules</code></strong></p>
|
||
<p>If you're not using a bundler but you're still running code in a web browser,
|
||
<code>wasm-bindgen</code> still supports this! For this use case you'll want to use the
|
||
<code>--target web</code> flag. You can check out a <a href="reference/../examples/without-a-bundler.html">full example</a> in the
|
||
documentation, but the highlights of this output are:</p>
|
||
<ul>
|
||
<li>When compiling you'll pass <code>--target web</code> to <code>wasm-pack</code> (or <code>wasm-bindgen</code>
|
||
directly).</li>
|
||
<li>The output can natively be included on a web page, and doesn't require any
|
||
further postprocessing. The output is included as an ES module.</li>
|
||
<li>The <code>--target web</code> mode is not able to use NPM dependencies.</li>
|
||
<li>You'll want to review the <a href="reference/browser-support.html">browser requirements</a> for <code>wasm-bindgen</code> because
|
||
no polyfills will be available.</li>
|
||
</ul>
|
||
<p>The CLI also supports an output mode called <code>--target no-modules</code> which is
|
||
similar to the <code>web</code> target in that it requires manual initialization of the
|
||
wasm and is intended to be included in web pages without any further
|
||
postprocessing. See the <a href="reference/../examples/without-a-bundler.html">without a bundler example</a> for some more
|
||
information about <code>--target no-modules</code>.</p>
|
||
<h2><a class="header" href="#nodejs" id="nodejs">Node.js</a></h2>
|
||
<p><strong><code>--target nodejs</code></strong></p>
|
||
<p>If you're deploying WebAssembly into Node.js (perhaps as an alternative to a
|
||
native module), then you'll want to pass the <code>--target nodejs</code> flag to
|
||
<code>wasm-pack</code> or <code>wasm-bindgen</code>.</p>
|
||
<p>Like the "without a bundler" strategy, this method of deployment does not
|
||
require any further postprocessing. The generated JS shims can be <code>require</code>'d
|
||
just like any other Node module (even the <code>*_bg</code> wasm file can be <code>require</code>'d
|
||
as it has a JS shim generated as well).</p>
|
||
<p>Note that this method requires a version of Node.js with WebAssembly support,
|
||
which is currently Node 8 and above.</p>
|
||
<h2><a class="header" href="#deno" id="deno">Deno</a></h2>
|
||
<p><strong><code>--target deno</code></strong></p>
|
||
<p>To deploy WebAssembly to Deno, use the <code>--target deno</code> flag.
|
||
To then import your module inside deno, use</p>
|
||
<pre><code class="language-ts">// @deno-types="./out/crate_name.d.ts"
|
||
import { yourFunction } from "./out/crate_name.js";
|
||
</code></pre>
|
||
<h2><a class="header" href="#npm" id="npm">NPM</a></h2>
|
||
<p>If you'd like to deploy compiled WebAssembly to NPM, then the tool for the job
|
||
is <a href="https://rustwasm.github.io/docs/wasm-pack/"><code>wasm-pack</code></a>. More information on this coming soon!</p>
|
||
<h1><a class="header" href="#js-snippets" id="js-snippets">JS Snippets</a></h1>
|
||
<p>Often when developing a crate you want to run on the web you'll want to include
|
||
some JS code here and there. While <a href="https://docs.rs/js-sys"><code>js-sys</code></a> and
|
||
<a href="https://docs.rs/web-sys"><code>web-sys</code></a> cover many needs they don't cover
|
||
everything, so <code>wasm-bindgen</code> supports the ability to write JS code next to your
|
||
Rust code and have it included in the final output artifact.</p>
|
||
<p>To include a local JS file, you'll use the <code>#[wasm_bindgen(module)]</code> macro:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(module = "/js/foo.js")]
|
||
extern "C" {
|
||
fn add(a: u32, b: u32) -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>This declaration indicates that all the functions contained in the <code>extern</code>
|
||
block are imported from the file <code>/js/foo.js</code>, where the root is relative to the
|
||
crate root (where <code>Cargo.toml</code> is located).</p>
|
||
<p>The <code>/js/foo.js</code> file will make its way to the final output when <code>wasm-bindgen</code>
|
||
executes, so you can use the <code>module</code> annotation in a library without having to
|
||
worry users of your library!</p>
|
||
<p>The JS file itself must be written with ES module syntax:</p>
|
||
<pre><code class="language-js">export function add(a, b) {
|
||
return a + b;
|
||
}
|
||
</code></pre>
|
||
<p>A full design of this feature can be found in <a href="https://github.com/rustwasm/rfcs/pull/6">RFC 6</a> as well if you're
|
||
interested!</p>
|
||
<h3><a class="header" href="#using-inline_js" id="using-inline_js">Using <code>inline_js</code></a></h3>
|
||
<p>In addition to <code>module = "..."</code> if you're a macro author you also have the
|
||
ability to use the <code>inline_js</code> attribute:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(inline_js = "export function add(a, b) { return a + b; }")]
|
||
extern "C" {
|
||
fn add(a: u32, b: u32) -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Using <code>inline_js</code> indicates that the JS module is specified inline in the
|
||
attribute itself, and no files are loaded from the filesystem. They have the
|
||
same limitations and caveats as when using <code>module</code>, but can sometimes be easier
|
||
to generate for macros themselves. It's not recommended for hand-written code to
|
||
make use of <code>inline_js</code> but instead to leverage <code>module</code> where possible.</p>
|
||
<h3><a class="header" href="#caveats" id="caveats">Caveats</a></h3>
|
||
<p>While quite useful local JS snippets currently suffer from a few caveats which
|
||
are important to be aware of. Many of these are temporary though!</p>
|
||
<ul>
|
||
<li>
|
||
<p>Currently <code>import</code> statements are not supported in the JS file. This is a
|
||
restriction we may lift in the future once we settle on a good way to support
|
||
this. For now, though, js snippets must be standalone modules and can't import
|
||
from anything else.</p>
|
||
</li>
|
||
<li>
|
||
<p>Only <code>--target web</code> and the default bundler output mode are supported. To
|
||
support <code>--target nodejs</code> we'd need to translate ES module syntax to CommonJS
|
||
(this is
|
||
planned to be done, just hasn't been done yet). Additionally to support
|
||
<code>--target no-modules</code> we'd have to similarly translate from ES modules to
|
||
something else.</p>
|
||
</li>
|
||
<li>
|
||
<p>Paths in <code>module = "..."</code> must currently start with <code>/</code>, or be rooted at the
|
||
crate root. It is intended to eventually support relative paths like <code>./</code> and
|
||
<code>../</code>, but it's currently believed that this requires more support in
|
||
the Rust <code>proc_macro</code> crate.</p>
|
||
</li>
|
||
</ul>
|
||
<p>As above, more detail about caveats can be found in <a href="https://github.com/rustwasm/rfcs/pull/6">RFC 6</a>.</p>
|
||
<h1><a class="header" href="#passing-rust-closures-to-imported-javascript-functions" id="passing-rust-closures-to-imported-javascript-functions">Passing Rust Closures to Imported JavaScript Functions</a></h1>
|
||
<p>The <code>#[wasm_bindgen]</code> attribute supports Rust closures being passed to
|
||
JavaScript in two variants:</p>
|
||
<ol>
|
||
<li>
|
||
<p>Stack-lifetime closures that should not be invoked by JavaScript again after
|
||
the imported JavaScript function that the closure was passed to returns.</p>
|
||
</li>
|
||
<li>
|
||
<p>Heap-allocated closures that can be invoked any number of times, but must be
|
||
explicitly deallocated when finished.</p>
|
||
</li>
|
||
</ol>
|
||
<h2><a class="header" href="#stack-lifetime-closures" id="stack-lifetime-closures">Stack-Lifetime Closures</a></h2>
|
||
<p>Closures with a stack lifetime are passed to JavaScript as either <code>&Fn</code> or <code>&mut FnMut</code> trait objects:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// Import JS functions that take closures
|
||
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
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;
|
||
});
|
||
#}</code></pre></pre>
|
||
<p><strong>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.</strong></p>
|
||
<p>Closures also support arguments and return values like exports do, for example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
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)
|
||
});
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#heap-allocated-closures" id="heap-allocated-closures">Heap-Allocated Closures</a></h2>
|
||
<p>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 <code>setTimeout</code>. For this, you want the imported function to
|
||
return but the JavaScript closure still needs to be valid!</p>
|
||
<p>For this scenario, you need the <code>Closure</code> type, which is defined in the
|
||
<code>wasm_bindgen</code> crate, exported in <code>wasm_bindgen::prelude</code>, and represents a
|
||
"long lived" closure.
|
||
The <code>Closure</code> type is currently behind a feature which needs to be enabled:</p>
|
||
<pre><code class="language-toml">[dependencies]
|
||
wasm-bindgen = {version = "^0.2", features = ["nightly"]}
|
||
</code></pre>
|
||
<p>The validity of the JavaScript closure is tied to the lifetime of the <code>Closure</code>
|
||
in Rust. <strong>Once a <code>Closure</code> 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.</strong></p>
|
||
<p>Like stack closures a <code>Closure</code> supports both <code>Fn</code> and <code>FnMut</code> closures, as well
|
||
as arguments and returns.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
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: 'static>(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"))
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#receiving-javascript-closures-in-exported-rust-functions" id="receiving-javascript-closures-in-exported-rust-functions">Receiving JavaScript Closures in Exported Rust Functions</a></h1>
|
||
<p>You can use the <code>js-sys</code> crate to access JavaScript's <code>Function</code> type, and
|
||
invoke that function via <code>Function.prototype.apply</code> and
|
||
<code>Function.prototype.call</code>.</p>
|
||
<p>For example, we can wrap a <code>Vec<u32></code> in a new type, export it to JavaScript,
|
||
and invoke a JavaScript closure on each member of the <code>Vec</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Since Rust has no function overloading, the <code>call#</code> method also requires a
|
||
number representing the amount of arguments passed to the JavaScript closure.</p>
|
||
<h1><a class="header" href="#working-with-a-js-promise-and-a-rust-future" id="working-with-a-js-promise-and-a-rust-future">Working with a JS <code>Promise</code> and a Rust <code>Future</code></a></h1>
|
||
<p>Many APIs on the web work with a <code>Promise</code>, such as an <code>async</code> function in JS.
|
||
Naturally you'll probably want to interoperate with them from Rust! To do that
|
||
you can use the <code>wasm-bindgen-futures</code> crate as well as Rust <code>async</code>
|
||
functions.</p>
|
||
<p>The first thing you might encounter is the need for working with a <code>Promise</code>.
|
||
For this you'll want to use <a href="https://docs.rs/js-sys/*/js_sys/struct.Promise.html"><code>js_sys::Promise</code></a>. Once you've got one of those
|
||
values you can convert that value to <code>wasm_bindgen_futures::JsFuture</code>. This type
|
||
implements the <code>std::future::Future</code> trait which allows naturally using it in an
|
||
<code>async</code> function. For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
async fn get_from_js() -> Result<JsValue, JsValue> {
|
||
let promise = js_sys::Promise::resolve(&42.into());
|
||
let result = wasm_bindgen_futures::JsFuture::from(promise).await?;
|
||
Ok(result)
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here we can see how converting a <code>Promise</code> to Rust creates a <code>impl Future<Output = Result<JsValue, JsValue>></code>. This corresponds to <code>then</code> and <code>catch</code> in JS where
|
||
a successful promise becomes <code>Ok</code> and an erroneous promise becomes <code>Err</code>.</p>
|
||
<p>Next up you'll probably want to export a Rust function to JS that returns a
|
||
promise. To do this you can use an <code>async</code> function and <code>#[wasm_bindgen]</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
pub async fn foo() {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>When invoked from JS the <code>foo</code> function here will return a <code>Promise</code>, so you can
|
||
import this as:</p>
|
||
<pre><code class="language-js">import { foo } from "my-module";
|
||
|
||
async function shim() {
|
||
const result = await foo();
|
||
// ...
|
||
}
|
||
</code></pre>
|
||
<h2><a class="header" href="#return-values-of-async-fn" id="return-values-of-async-fn">Return values of <code>async fn</code></a></h2>
|
||
<p>When using an <code>async fn</code> in Rust and exporting it to JS there's some
|
||
restrictions on the return type. The return value of an exported Rust function
|
||
will eventually become <code>Result<JsValue, JsValue></code> where <code>Ok</code> turns into a
|
||
successfully resolved promise and <code>Err</code> is equivalent to throwing an exception.</p>
|
||
<p>The following types are supported as return types from an <code>async fn</code>:</p>
|
||
<ul>
|
||
<li><code>()</code> - turns into a successful <code>undefined</code> in JS</li>
|
||
<li><code>T: Into<JsValue></code> - turns into a successful JS value</li>
|
||
<li><code>Result<(), E: Into<JsValue>></code> - if <code>Ok(())</code> turns into a successful
|
||
<code>undefined</code> and otherwise turns into a failed promise with <code>E</code> converted to a
|
||
JS value</li>
|
||
<li><code>Result<T: Into<JsValue>, E: Into<JsValue>></code> - like the previous case except
|
||
both data payloads are converted into a <code>JsValue</code>.</li>
|
||
</ul>
|
||
<p>Note that many types implement being converted into a <code>JsValue</code>, such as all
|
||
imported types via <code>#[wasm_bindgen]</code> (aka those in <code>js-sys</code> or <code>web-sys</code>),
|
||
primitives like <code>u32</code>, and all exported <code>#[wasm_bindgen]</code> types. In general,
|
||
you should be able to write code without having too many explicit conversions,
|
||
and the macro should take care of the rest!</p>
|
||
<h2><a class="header" href="#using-wasm-bindgen-futures" id="using-wasm-bindgen-futures">Using <code>wasm-bindgen-futures</code></a></h2>
|
||
<p>The <code>wasm-bindgen-futures</code> crate bridges the gap between JavaScript <code>Promise</code>s
|
||
and Rust <code>Future</code>s. Its <code>JsFuture</code> type provides conversion from a JavaScript
|
||
<code>Promise</code> into a Rust <code>Future</code>, and its <code>future_to_promise</code> function converts a
|
||
Rust <code>Future</code> into a JavaScript <code>Promise</code> and schedules it to be driven to
|
||
completion.</p>
|
||
<p>Learn more:</p>
|
||
<ul>
|
||
<li><a href="https://crates.io/crates/wasm-bindgen-futures"><code>wasm_bindgen_futures</code> on crates.io</a></li>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/"><code>wasm-bindgen-futures</code> API documentation and example usage</a></li>
|
||
</ul>
|
||
<h2><a class="header" href="#compatibility-with-versions-of-future" id="compatibility-with-versions-of-future">Compatibility with versions of <code>Future</code></a></h2>
|
||
<p>The current crate on crates.io, <code>wasm-bindgen-futures 0.4.*</code>, supports
|
||
<code>std::future::Future</code> and <code>async</code>/<code>await</code> in Rust. This typically requires Rust
|
||
1.39.0+ (as of this writing on 2019-09-05 it's the nightly channel of Rust).</p>
|
||
<p>If you're using the <code>Future</code> trait from the <code>futures</code> <code>0.1.*</code> crate then you'll
|
||
want to use the <code>0.3.*</code> track of <code>wasm-bindgen-futures</code> on crates.io.</p>
|
||
<h1><a class="header" href="#iterating-over-javascript-values" id="iterating-over-javascript-values">Iterating over JavaScript Values</a></h1>
|
||
<h2><a class="header" href="#methods-that-return-js_sysiterator" id="methods-that-return-js_sysiterator">Methods That Return <code>js_sys::Iterator</code></a></h2>
|
||
<p>Some JavaScript collections have methods for iterating over their values or
|
||
keys:</p>
|
||
<ul>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Map.html#method.values"><code>Map::values</code></a></li>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Set.html#method.keys"><code>Set::keys</code></a></li>
|
||
<li>etc...</li>
|
||
</ul>
|
||
<p>These methods return
|
||
<a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Iterator.html"><code>js_sys::Iterator</code></a>,
|
||
which is the Rust representation of a JavaScript object that has a <code>next</code> method
|
||
that either returns the next item in the iteration, notes that iteration has
|
||
completed, or throws an error. That is, <code>js_sys::Iterator</code> represents an object
|
||
that implements <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols">the duck-typed JavaScript iteration
|
||
protocol</a>.</p>
|
||
<p><code>js_sys::Iterator</code> can be converted into a Rust iterator either by reference
|
||
(into
|
||
<a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Iter.html"><code>js_sys::Iter<'a></code></a>)
|
||
or by value (into
|
||
<a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.IntoIter.html"><code>js_sys::IntoIter</code></a>). The
|
||
Rust iterator will yield items of type <code>Result<JsValue></code>. If it yields an
|
||
<code>Ok(...)</code>, then the JS iterator protocol returned an element. If it yields an
|
||
<code>Err(...)</code>, then the JS iterator protocol threw an exception.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
extern crate js_sys;
|
||
extern crate wasm_bindgen;
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn count_strings_in_set(set: &js_sys::Set) -> u32 {
|
||
let mut count = 0;
|
||
|
||
// Call `keys` to get an iterator over the set's elements. Because this is
|
||
// in a `for ... in ...` loop, Rust will automatically call its
|
||
// `IntoIterator` trait implementation to convert it into a Rust iterator.
|
||
for x in set.keys() {
|
||
// We know the built-in iterator for set elements won't throw
|
||
// exceptions, so just unwrap the element. If this was an untrusted
|
||
// iterator, we might want to explicitly handle the case where it throws
|
||
// an exception instead of returning a `{ value, done }` object.
|
||
let x = x.unwrap();
|
||
|
||
// If `x` is a string, increment our count of strings in the set!
|
||
if x.is_string() {
|
||
count += 1;
|
||
}
|
||
}
|
||
|
||
count
|
||
}
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#iterating-over-uanyu-javascript-object-that-implements-the-iterator-protocol" id="iterating-over-uanyu-javascript-object-that-implements-the-iterator-protocol">Iterating Over <u>Any</u> JavaScript Object that Implements the Iterator Protocol</a></h2>
|
||
<p>You could manually test for whether an object implements JS's duck-typed
|
||
iterator protocol, and if so, convert it into a <code>js_sys::Iterator</code> that you can
|
||
finally iterate over. You don't need to do this by-hand, however, since we
|
||
bundled this up as <a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/fn.try_iter.html">the <code>js_sys::try_iter</code>
|
||
function!</a></p>
|
||
<p>For example, we can write a function that collects the numbers from any JS
|
||
iterable and returns them as an <code>Array</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
extern crate js_sys;
|
||
extern crate wasm_bindgen;
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn collect_numbers(some_iterable: &JsValue) -> Result<js_sys::Array, JsValue> {
|
||
let nums = js_sys::Array::new();
|
||
|
||
let iterator = js_sys::try_iter(some_iterable)?.ok_or_else(|| {
|
||
"need to pass iterable JS values!"
|
||
})?;
|
||
|
||
for x in iterator {
|
||
// If the iterator's `next` method throws an error, propagate it
|
||
// up to the caller.
|
||
let x = x?;
|
||
|
||
// If `x` is a number, add it to our array of numbers!
|
||
if x.as_f64().is_some() {
|
||
nums.push(&x);
|
||
}
|
||
}
|
||
|
||
Ok(nums)
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#serializing-and-deserializing-arbitrary-data-into-and-from-jsvalue-with-serde" id="serializing-and-deserializing-arbitrary-data-into-and-from-jsvalue-with-serde">Serializing and Deserializing Arbitrary Data Into and From <code>JsValue</code> with Serde</a></h1>
|
||
<p>It's possible to pass arbirtrary data from Rust to JavaScript by serializing it
|
||
to JSON with <a href="https://github.com/serde-rs/serde">Serde</a>. <code>wasm-bindgen</code> includes
|
||
the <code>JsValue</code> type, which streamlines serializing and deserializing.</p>
|
||
<h2><a class="header" href="#enable-the-serde-serialize-feature" id="enable-the-serde-serialize-feature">Enable the <code>"serde-serialize"</code> Feature</a></h2>
|
||
<p>To enable the <code>"serde-serialize"</code> feature, do two things in <code>Cargo.toml</code>:</p>
|
||
<ol>
|
||
<li>Add the <code>serde</code> and <code>serde_derive</code> crates to <code>[dependencies]</code>.</li>
|
||
<li>Add <code>features = ["serde-serialize"]</code> to the existing <code>wasm-bindgen</code>
|
||
dependency.</li>
|
||
</ol>
|
||
<pre><code class="language-toml">[dependencies]
|
||
serde = "^1.0.59"
|
||
serde_derive = "^1.0.59"
|
||
|
||
[dependencies.wasm-bindgen]
|
||
version = "^0.2"
|
||
features = ["serde-serialize"]
|
||
</code></pre>
|
||
<h2><a class="header" href="#import-serdes-custom-derive-macros" id="import-serdes-custom-derive-macros">Import Serde's Custom-Derive Macros</a></h2>
|
||
<p>In your top-level Rust file (e.g. <code>lib.rs</code> or <code>main.rs</code>), enable the <code>Serialize</code>
|
||
and <code>Deserialize</code> custom-derive macros:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[macro_use]
|
||
extern crate serde_derive;
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#derive-the-serialize-and-deserialize-traits" id="derive-the-serialize-and-deserialize-traits">Derive the <code>Serialize</code> and <code>Deserialize</code> Traits</a></h2>
|
||
<p>Add <code>#[derive(Serialize, Deserialize)]</code> to your type. All of your type's
|
||
members must also be supported by Serde, i.e. their types must also implement
|
||
the <code>Serialize</code> and <code>Deserialize</code> traits.</p>
|
||
<p>For example, let's say we'd like to pass this <code>struct</code> to JavaScript; doing so
|
||
is not possible in <code>wasm-bindgen</code> normally due to the use of <code>HashMap</code>s, arrays,
|
||
and nested <code>Vec</code>s. None of those types are supported for sending across the wasm
|
||
ABI naively, but all of them implement Serde's <code>Serialize</code> and <code>Deserialize</code>.</p>
|
||
<p>Note that we do not need to use the <code>#[wasm_bindgen]</code> macro.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[derive(Serialize)]
|
||
pub struct Example {
|
||
pub field1: HashMap<u32, String>,
|
||
pub field2: Vec<Vec<f32>>,
|
||
pub field3: [f32; 4],
|
||
}
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#send-it-to-javascript-with-jsvaluefrom_serde" id="send-it-to-javascript-with-jsvaluefrom_serde">Send it to JavaScript with <code>JsValue::from_serde</code></a></h2>
|
||
<p>Here's a function that will pass an <code>Example</code> to JavaScript by serializing it to
|
||
<code>JsValue</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[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()
|
||
}
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#receive-it-from-javascript-with-jsvalueinto_serde" id="receive-it-from-javascript-with-jsvalueinto_serde">Receive it from JavaScript with <code>JsValue::into_serde</code></a></h2>
|
||
<p>Here's a function that will receive a <code>JsValue</code> parameter from JavaScript and
|
||
then deserialize an <code>Example</code> from it:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
pub fn receive_example_from_js(val: &JsValue) {
|
||
let example: Example = val.into_serde().unwrap();
|
||
...
|
||
}
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#javascript-usage" id="javascript-usage">JavaScript Usage</a></h2>
|
||
<p>In the <code>JsValue</code> that JavaScript gets, <code>field1</code> will be an <code>Object</code> (not a
|
||
JavaScript <code>Map</code>), <code>field2</code> will be a JavaScript <code>Array</code> whose members are
|
||
<code>Array</code>s of numbers, and <code>field3</code> will be an <code>Array</code> of numbers.</p>
|
||
<pre><code class="language-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<Vec<f32>>"
|
||
example.field2.push([5,6]);
|
||
|
||
// Send the example object back to wasm.
|
||
receive_example_from_js(example);
|
||
</code></pre>
|
||
<h2><a class="header" href="#an-alternative-approach-serde-wasm-bindgen" id="an-alternative-approach-serde-wasm-bindgen">An Alternative Approach: <code>serde-wasm-bindgen</code></a></h2>
|
||
<p><a href="https://github.com/cloudflare/serde-wasm-bindgen">The <code>serde-wasm-bindgen</code>
|
||
crate</a> serializes and
|
||
deserializes Rust structures directly to <code>JsValue</code>s, without going through
|
||
temporary JSON stringification. This approach has both advantages and
|
||
disadvantages.</p>
|
||
<p>The primary advantage is smaller code size: going through JSON entrenches code
|
||
to stringify and parse floating point numbers, which is not a small amount of
|
||
code. It also supports more types than JSON does, such as <code>Map</code>, <code>Set</code>, and
|
||
array buffers.</p>
|
||
<p>There are two primary disadvantages. The first is that it is not always
|
||
compatible with the default JSON-based serialization. The second is that it
|
||
performs more calls back and forth between JS and Wasm, which has not been fully
|
||
optimized in all engines, meaning it can sometimes be a speed
|
||
regression. However, in other cases, it is a speed up over the JSON-based
|
||
stringification, so — as always — make sure to profile your own use
|
||
cases as necessary.</p>
|
||
<h1><a class="header" href="#accessing-properties-of-untyped-javascript-values" id="accessing-properties-of-untyped-javascript-values">Accessing Properties of Untyped JavaScript Values</a></h1>
|
||
<p>To read and write arbitrary properties from any untyped JavaScript value
|
||
regardless if it is an <code>instanceof</code> some JavaScript class or not, use <a href="https://docs.rs/js-sys/latest/js_sys/Reflect/index.html">the
|
||
<code>js_sys::Reflect</code> APIs</a>. These APIs are bindings to the
|
||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect">JavaScript builtin <code>Reflect</code> object</a> and its methods.</p>
|
||
<p>You might also benefit from <a href="reference/./working-with-duck-typed-interfaces.html">using duck-typed
|
||
interfaces</a> instead of working with
|
||
untyped values.</p>
|
||
<h2><a class="header" href="#reading-properties-with-js_sysreflectget" id="reading-properties-with-js_sysreflectget">Reading Properties with <code>js_sys::Reflect::get</code></a></h2>
|
||
<p><a href="https://docs.rs/js-sys/0.3.39/js_sys/Reflect/fn.get.html">API documentation for <code>js_sys::Reflect::get</code>.</a></p>
|
||
<p>A function that returns the value of a property.</p>
|
||
<h4><a class="header" href="#rust-usage" id="rust-usage">Rust Usage</a></h4>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
let value = js_sys::Reflect::get(&target, &property_key)?;
|
||
#}</code></pre></pre>
|
||
<h4><a class="header" href="#javascript-equivalent" id="javascript-equivalent">JavaScript Equivalent</a></h4>
|
||
<pre><code class="language-js">let value = target[property_key];
|
||
</code></pre>
|
||
<h2><a class="header" href="#writing-properties-with-js_sysreflectset" id="writing-properties-with-js_sysreflectset">Writing Properties with <code>js_sys::Reflect::set</code></a></h2>
|
||
<p><a href="https://docs.rs/js-sys/0.3.39/js_sys/Reflect/fn.set.html">API documentation for <code>js_sys::Reflect::set</code>.</a></p>
|
||
<p>A function that assigns a value to a property. Returns a boolean that is true if
|
||
the update was successful.</p>
|
||
<h4><a class="header" href="#rust-usage-1" id="rust-usage-1">Rust Usage</a></h4>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
js_sys::Reflect::set(&target, &property_key, &value)?;
|
||
#}</code></pre></pre>
|
||
<h4><a class="header" href="#javascript-equivalent-1" id="javascript-equivalent-1">JavaScript Equivalent</a></h4>
|
||
<pre><code class="language-js">target[property_key] = value;
|
||
</code></pre>
|
||
<h2><a class="header" href="#determining-if-a-property-exists-with-js_sysreflecthas" id="determining-if-a-property-exists-with-js_sysreflecthas">Determining if a Property Exists with <code>js_sys::Reflect::has</code></a></h2>
|
||
<p><a href="https://docs.rs/js-sys/0.3.39/js_sys/Reflect/fn.has.html">API documentation for <code>js_sys::Reflect::has</code>.</a></p>
|
||
<p>The JavaScript <code>in</code> operator as function. Returns a boolean indicating whether
|
||
an own or inherited property exists on the target.</p>
|
||
<h4><a class="header" href="#rust-usage-2" id="rust-usage-2">Rust Usage</a></h4>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
if js_sys::Reflect::has(&target, &property_key)? {
|
||
// ...
|
||
} else {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<h4><a class="header" href="#javascript-equivalent-2" id="javascript-equivalent-2">JavaScript Equivalent</a></h4>
|
||
<pre><code class="language-js">if (property_key in target) {
|
||
// ...
|
||
} else {
|
||
// ...
|
||
}
|
||
</code></pre>
|
||
<h2><a class="header" href="#but-wait--theres-more" id="but-wait--theres-more">But wait — there's more!</a></h2>
|
||
<p>See <a href="https://docs.rs/js-sys/latest/js_sys/Reflect/index.html">the <code>js_sys::Reflect</code> API documentation</a> for the full
|
||
listing of JavaScript value reflection and introspection capabilities.</p>
|
||
<h1><a class="header" href="#working-with-duck-typed-interfaces" id="working-with-duck-typed-interfaces">Working with Duck-Typed Interfaces</a></h1>
|
||
<p>Liberal use of <a href="reference/./attributes/on-js-imports/structural.html">the <code>structural</code>
|
||
attribute</a> 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 <code>instanceof</code> the
|
||
same base can be used the same way.</p>
|
||
<h2><a class="header" href="#defining-a-duck-typed-interface-in-rust" id="defining-a-duck-typed-interface-in-rust">Defining a Duck-Typed Interface in Rust</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
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 "C" {
|
||
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();
|
||
// ...
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#javascript-usage-1" id="javascript-usage-1">JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">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: () => "<corgi.gif>"
|
||
};
|
||
|
||
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);
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#the-wasm-bindgen-command-line-interface" id="the-wasm-bindgen-command-line-interface">The <code>wasm-bindgen</code> Command Line Interface</a></h1>
|
||
<p>The <code>wasm-bindgen</code> command line tool has a number of options available to it to
|
||
tweak the JavaScript that is generated. The most up-to-date set of flags can
|
||
always be listed via <code>wasm-bindgen --help</code>.</p>
|
||
<blockquote>
|
||
<p>Note: usually, one should use a <a href="https://github.com/rustwasm/wasm-pack"><code>wasm-pack</code>-based workflow</a> rather
|
||
than running the <code>wasm-bindgen</code> command line tool by hand.</p>
|
||
</blockquote>
|
||
<h2><a class="header" href="#installation" id="installation">Installation</a></h2>
|
||
<p>The recommend way to install the <code>wasm-bindgen</code> command line tool is with the
|
||
<code>wasm-pack</code> installer described
|
||
<a href="https://rustwasm.github.io/wasm-pack/installer/">here</a>. After installing
|
||
<code>wasm-pack</code>, you are ready to build project invoking <code>wasm-pack build</code>.
|
||
This command installs apropriate version of the <code>wasm-bindgen</code> command-line
|
||
tool. The version of <code>wasm-bindgen</code> installed by <code>wasm-pack</code> is not available
|
||
to be used directly via command line.</p>
|
||
<p>It is not recommended to install <code>wasm-bindgen-cli</code> as its version must match
|
||
<em>exactly</em> the version of <code>wasm-bindgen</code> that is specified in the project's
|
||
cargo.lock file. Using <code>wasm-pack</code> for building simplifies the build process
|
||
as <code>wasm-pack</code> ensures that the proper version of <code>wasm-bindgen</code> command-line
|
||
tool is used. That means that <code>wasm-pack</code> may install many different versions
|
||
of <code>wasm-bindgen</code>, but during the build <code>wasm-pack</code> will always make sure to
|
||
use the correct one.</p>
|
||
<p>Note: if, for any reason, you decide to use wasm-bindgen directly (this is
|
||
not recommended!) you will have to manually take care of using exactly the
|
||
same version of wasm-bindgen command-line tool (wasm-bindgen-cli) that
|
||
matches the version of wasm-bingden in cargo.lock.</p>
|
||
<h2><a class="header" href="#usage" id="usage">Usage</a></h2>
|
||
<pre><code>wasm-bindgen [options] ./target/wasm32-unknown-unknown/release/crate.wasm
|
||
</code></pre>
|
||
<h2><a class="header" href="#options" id="options">Options</a></h2>
|
||
<h3><a class="header" href="#--out-dir-dir" id="--out-dir-dir"><code>--out-dir DIR</code></a></h3>
|
||
<p>The target directory to emit the JavaScript bindings, TypeScript definitions,
|
||
processed <code>.wasm</code> binary, etc...</p>
|
||
<h3><a class="header" href="#--target" id="--target"><code>--target</code></a></h3>
|
||
<p>This flag indicates what flavor of output what <code>wasm-bindgen</code> should generate.
|
||
For example it could generate code to be loaded in a bundler like Webpack, a
|
||
native web page, or Node.js. For a full list of options to pass this flag, see
|
||
the section on <a href="reference/deployment.html">deployment</a></p>
|
||
<h3><a class="header" href="#--no-modules-global-var" id="--no-modules-global-var"><code>--no-modules-global VAR</code></a></h3>
|
||
<p>When <code>--target no-modules</code> is used this flag can indicate what the name of the
|
||
global to assign generated bindings to.</p>
|
||
<p>For more information about this see the section on <a href="reference/deployment.html">deployment</a></p>
|
||
<h3><a class="header" href="#--typescript" id="--typescript"><code>--typescript</code></a></h3>
|
||
<p>Output a TypeScript declaration file for the generated JavaScript bindings. This
|
||
is on by default.</p>
|
||
<h3><a class="header" href="#--no-typescript" id="--no-typescript"><code>--no-typescript</code></a></h3>
|
||
<p>By default, a <code>*.d.ts</code> TypeScript declaration file is generated for the
|
||
generated JavaScript bindings, but this flag will disable that.</p>
|
||
<h3><a class="header" href="#--omit-imports" id="--omit-imports"><code>--omit-imports</code></a></h3>
|
||
<p>When the <code>module</code> attribute is used with the <code>wasm-bindgen</code> macro, the code
|
||
generator will emit corresponding <code>import</code> or <code>require</code> statements in the header
|
||
section of the generated javascript. This flag causes those import statements to
|
||
be omitted. This is necessary for some use cases, such as generating javascript
|
||
which is intended to be used with Electron (with node integration disabled),
|
||
where the imports are instead handled through a separate preload script.</p>
|
||
<h3><a class="header" href="#--debug" id="--debug"><code>--debug</code></a></h3>
|
||
<p>Generates a bit more JS and wasm in "debug mode" to help catch programmer
|
||
errors, but this output isn't intended to be shipped to production.</p>
|
||
<h3><a class="header" href="#--no-demangle" id="--no-demangle"><code>--no-demangle</code></a></h3>
|
||
<p>When post-processing the <code>.wasm</code> binary, do not demangle Rust symbols in the
|
||
"names" custom section.</p>
|
||
<h3><a class="header" href="#--keep-debug" id="--keep-debug"><code>--keep-debug</code></a></h3>
|
||
<p>When post-processing the <code>.wasm</code> binary, do not strip DWARF debug info custom
|
||
sections.</p>
|
||
<h3><a class="header" href="#--browser" id="--browser"><code>--browser</code></a></h3>
|
||
<p>When generating bundler-compatible code (see the section on <a href="reference/deployment.html">deployment</a>) this
|
||
indicates that the bundled code is always intended to go into a browser so a few
|
||
checks for Node.js can be elided.</p>
|
||
<h1><a class="header" href="#optimizing-for-size-with-wasm-bindgen" id="optimizing-for-size-with-wasm-bindgen">Optimizing for Size with <code>wasm-bindgen</code></a></h1>
|
||
<p>The Rust and WebAssembly Working Group's <a href="https://rustwasm.github.io/book/game-of-life/introduction.html">Game of Life tutorial</a> has an
|
||
excellent section on <a href="https://rustwasm.github.io/book/game-of-life/code-size.html">shrinking wasm code size</a>, but there's a few
|
||
<code>wasm-bindgen</code>-specific items to mention as well!</p>
|
||
<p>First and foremost, <code>wasm-bindgen</code> is designed to be lightweight and a "pay only
|
||
for what you use" mentality. If you suspect that <code>wasm-bindgen</code> is bloating your
|
||
program that is a bug and we'd like to know about it! Please feel free to <a href="https://github.com/rustwasm/wasm-bindgen/issues/new">file
|
||
an issue</a>, even if it's a question!</p>
|
||
<h3><a class="header" href="#what-to-profile" id="what-to-profile">What to profile</a></h3>
|
||
<p>With <code>wasm-bindgen</code> there's a few different files to be measuring the size of.
|
||
The first of which is the output of the compiler itself, typically at
|
||
<code>target/wasm32-unknown-unknown/release/foo.wasm</code>. <strong>This file is not optimized
|
||
for size and you should not measure it.</strong> The output of the compiler when
|
||
linking with <code>wasm-bindgen</code> is by design larger than it needs to be, the
|
||
<code>wasm-bindgen</code> CLI tool will automatically strip all unneeded functionality out
|
||
of the binary.</p>
|
||
<p>This leaves us with two primary generated files to measure the size of:</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Generated wasm</strong> - after running the <code>wasm-bindgen</code> CLI tool you'll get a
|
||
file in <code>--out-dir</code> that looks like <code>foo_bg.wasm</code>. This file is the final
|
||
fully-finished artifact from <code>wasm-bindgen</code>, and it reflects the size of the
|
||
app you'll be publishing. All the optimizations <a href="https://rustwasm.github.io/book/game-of-life/code-size.html">mentioned in the code size
|
||
tutorial</a> will help reduce the size of this binary, so feel free to go
|
||
crazy!</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Generated JS</strong> - the other file after running <code>wasm-bindgen</code> is a <code>foo.js</code>
|
||
file which is what's actually imported by other JS code. This file is already
|
||
generated to be as small as possible (not including unneeded functionality).
|
||
The JS, however, is not uglified or minified, but rather still human readable
|
||
and debuggable. It's expected that you'll run an uglifier or bundler of the JS
|
||
output to minimize it further in your application. If you spot a way we could
|
||
reduce the output JS size further (or make it more amenable to bundler
|
||
minification), please let us know!</p>
|
||
</li>
|
||
</ul>
|
||
<h3><a class="header" href="#example" id="example">Example</a></h3>
|
||
<p>As an example, the <code>wasm-bindgen</code> repository <a href="https://rustwasm.github.io/docs/wasm-bindgen/examples/add.html">contains an example</a>
|
||
about generating small wasm binaries and shows off how to generate a small wasm
|
||
file for adding two numbers.</p>
|
||
<h1><a class="header" href="#supported-rust-targets" id="supported-rust-targets">Supported Rust Targets</a></h1>
|
||
<blockquote>
|
||
<p><strong>Note</strong>: This section is about Rust target triples, not targets like node/web
|
||
workers/browsers. More information on that coming soon!</p>
|
||
</blockquote>
|
||
<p>The <code>wasm-bindgen</code> project is designed to target the <code>wasm32-unknown-unknown</code>
|
||
target in Rust. This target is a "bare bones" target for Rust which emits
|
||
WebAssembly as output. The standard library is largely inert as modules like
|
||
<code>std::fs</code> and <code>std::net</code> will simply return errors.</p>
|
||
<h2><a class="header" href="#non-wasm-targets" id="non-wasm-targets">Non-wasm targets</a></h2>
|
||
<p>Note that <code>wasm-bindgen</code> also aims to compile on all targets. This means that it
|
||
should be safe, if you like, to use <code>#[wasm_bindgen]</code> even when compiling for
|
||
Windows (for example). For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">#[wasm_bindgen]
|
||
pub fn add(a: u32, b: u32) -> u32 {
|
||
a + b
|
||
}
|
||
|
||
#[cfg(not(target_arch = "wasm32"))]
|
||
fn main() {
|
||
println!("1 + 2 = {}", add(1, 2));
|
||
}
|
||
</code></pre></pre>
|
||
<p>This program will compile and work on all platforms, not just
|
||
<code>wasm32-unknown-unknown</code>. Note that imported functions with <code>#[wasm_bindgen]</code>
|
||
will unconditionally panic on non-wasm targets. For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
}
|
||
|
||
fn main() {
|
||
log("hello!");
|
||
}
|
||
</code></pre></pre>
|
||
<p>This program will unconditionally panic on all platforms other than
|
||
<code>wasm32-unknown-unknown</code>.</p>
|
||
<p>For better compile times, however, you likely want to only use <code>#[wasm_bindgen]</code>
|
||
on the <code>wasm32-unknown-unknown</code> target. You can have a target-specific
|
||
dependency like so:</p>
|
||
<pre><code class="language-toml">[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||
wasm-bindgen = "0.2"
|
||
</code></pre>
|
||
<p>And in your code you can use:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[cfg(target_arch = "wasm32")]
|
||
#[wasm_bindgen]
|
||
pub fn only_on_the_wasm_target() {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#other-web-targets" id="other-web-targets">Other Web Targets</a></h2>
|
||
<p>The <code>wasm-bindgen</code> target does not support the <code>wasm32-unknown-emscripten</code> nor
|
||
the <code>asmjs-unknown-emscripten</code> targets. There are currently no plans to support
|
||
these targets either. All annotations work like other platforms on the targets,
|
||
retaining exported functions and causing all imports to panic.</p>
|
||
<h1><a class="header" href="#supported-browsers" id="supported-browsers">Supported Browsers</a></h1>
|
||
<p>The output of <code>wasm-bindgen</code> includes a JS file, and as a result it's good to
|
||
know what browsers that file is expected to be used in! By default the output
|
||
uses ES modules which isn't implemented in all browsers today, but when using a
|
||
bundler (like Webpack) you should be able to produce output suitable for all
|
||
browsers.</p>
|
||
<p>Firefox, Chrome, Safari, and Edge browsers are all supported by
|
||
<code>wasm-bindgen</code>. If you find a problem in one of these browsers please <a href="https://github.com/rustwasm/wasm-bindgen/issues/new">report
|
||
it</a> as we'd like to fix the bug! If you find a bug in another browser we would
|
||
also like to be aware of it!</p>
|
||
<h2><a class="header" href="#caveats-1" id="caveats-1">Caveats</a></h2>
|
||
<ul>
|
||
<li>
|
||
<p><strong>IE 11</strong> - <code>wasm-bindgen</code> by default requires support for
|
||
<code>WebAssembly</code>, but no version of IE currently supports <code>WebAssembly</code>. You can
|
||
support IE by <a href="https://github.com/WebAssembly/binaryen">compiling wasm files to JS using <code>wasm2js</code></a> (you can <a href="reference/../examples/wasm2js.html">see
|
||
an example of doing this too</a>). Note
|
||
that at this time no bundler will do this by default, but we'd love to
|
||
document plugins which do this if you are aware of one!</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Edge</strong> - the <code>TextEncoder</code> and <code>TextDecoder</code> APIs are not currently
|
||
available in Edge which <code>wasm-bindgen</code> uses to encode/decode strings between
|
||
JS and Rust. You can polyfill this with at least one of two strategies:</p>
|
||
<ol>
|
||
<li>
|
||
<p>If using a bundler, you can likely configure the bundler to polyfill these
|
||
types by default. For example if you're using Webpack you can use the
|
||
<a href="https://webpack.js.org/plugins/provide-plugin/"><code>ProvidePlugin</code> interface</a> like so after also adding
|
||
<a href="https://www.npmjs.com/package/text-encoding"><code>text-encoding</code></a> to your <code>package.json</code></p>
|
||
<pre><code class="language-js">const webpack = require('webpack');
|
||
|
||
module.exports = {
|
||
plugins: [
|
||
new webpack.ProvidePlugin({
|
||
TextDecoder: ['text-encoding', 'TextDecoder'],
|
||
TextEncoder: ['text-encoding', 'TextEncoder']
|
||
})
|
||
]
|
||
|
||
// ... other configuration options
|
||
};
|
||
</code></pre>
|
||
<p><strong>Warning:</strong> doing this implies the polyfill will always be used,
|
||
even if native APIs are available. This has a very significant
|
||
performance impact (the polyfill was measured to be 100x slower in Chromium)!</p>
|
||
</li>
|
||
<li>
|
||
<p>If you're not using a bundler you can also include support manually by
|
||
adding a <code><script></code> tag which defines the <code>TextEncoder</code> and <code>TextDecoder</code>
|
||
globals. <a href="https://stackoverflow.com/questions/40662142/polyfill-for-textdecoder/46549188#46549188">This StackOverflow question</a> has some example usage and MDN
|
||
has a <a href="https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder#Polyfill"><code>TextEncoder</code> polyfill implementation</a> to get you started
|
||
as well.</p>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>
|
||
<p><strong>BigInt and <code>u64</code></strong> - currently the WebAssembly specification for the web
|
||
forbids the usage of 64-bit integers (Rust types <code>i64</code> and <code>u64</code>) in
|
||
exported/imported functions. When using <code>wasm-bindgen</code>, however, <code>u64</code> is
|
||
allowed! The reason for this is that it's translated to the <code>BigInt</code> type in
|
||
JS. The <code>BigInt</code> class, however, is only currently supported in Chrome 67+ and
|
||
Firefox 68+ (as of the time of this writing) and isn't supported in Edge or
|
||
Safari, for example. For more, up-to-date details, see <a href="https://caniuse.com/#feat=bigint"><code>BigInt</code> on Can I
|
||
use...</a>.</p>
|
||
</li>
|
||
</ul>
|
||
<p>If you find other incompatibilities please report them to us! We'd love to
|
||
either keep this list up-to-date or fix the underlying bugs :)</p>
|
||
<h1><a class="header" href="#supported-rust-types-and-their-javascript-representations" id="supported-rust-types-and-their-javascript-representations">Supported Rust Types and their JavaScript Representations</a></h1>
|
||
<p>This section provides an overview of all the types that <code>wasm-bindgen</code> can send
|
||
and receive across the WebAssembly ABI boundary, and how they translate into
|
||
JavaScript.</p>
|
||
<h1><a class="header" href="#imported-extern-whatever-javascript-types" id="imported-extern-whatever-javascript-types">Imported <code>extern Whatever;</code> JavaScript Types</a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Instances of the extant <code>Whatever</code> JavaScript class / prototype constructor</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage" id="example-rust-usage">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub enum NumberEnum {
|
||
Foo = 0,
|
||
Bar = 1,
|
||
Qux = 2,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub enum StringEnum {
|
||
Foo = "foo",
|
||
Bar = "bar",
|
||
Qux = "qux",
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub struct Struct {
|
||
pub number: NumberEnum,
|
||
pub string: StringEnum,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
pub type SomeJsType;
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn imported_type_by_value(x: SomeJsType) {
|
||
/* ... */
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn imported_type_by_shared_ref(x: &SomeJsType) {
|
||
/* ... */
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_imported_type() -> SomeJsType {
|
||
unimplemented!()
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_option_imported_type(x: Option<SomeJsType>) {
|
||
/* ... */
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_option_imported_type() -> Option<SomeJsType> {
|
||
unimplemented!()
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage" id="example-javascript-usage">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
imported_type_by_value,
|
||
imported_type_by_shared_ref,
|
||
return_imported_type,
|
||
take_option_imported_type,
|
||
return_option_imported_type,
|
||
} from './guide_supported_types_examples';
|
||
|
||
imported_type_by_value(new SomeJsType());
|
||
imported_type_by_shared_ref(new SomeJsType());
|
||
|
||
let x = return_imported_type();
|
||
console.log(x instanceof SomeJsType); // true
|
||
|
||
take_option_imported_type(null);
|
||
take_option_imported_type(undefined);
|
||
take_option_imported_type(new SomeJsType());
|
||
|
||
let y = return_option_imported_type();
|
||
if (y == null) {
|
||
// ...
|
||
} else {
|
||
console.log(y instanceof SomeJsType); // true
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#exported-struct-whatever-rust-types" id="exported-struct-whatever-rust-types">Exported <code>struct Whatever</code> Rust Types</a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Instances of a <code>wasm-bindgen</code>-generated JavaScript <code>class Whatever { ... }</code></td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-1" id="example-rust-usage-1">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub struct ExportedNamedStruct {
|
||
pub inner: u32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn named_struct_by_value(x: ExportedNamedStruct) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn named_struct_by_shared_ref(x: &ExportedNamedStruct) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn named_struct_by_exclusive_ref(x: &mut ExportedNamedStruct) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_named_struct(inner: u32) -> ExportedNamedStruct {
|
||
ExportedNamedStruct { inner }
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub struct ExportedTupleStruct(pub u32, pub u32);
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_tuple_struct(x: u32, y: u32) -> ExportedTupleStruct {
|
||
ExportedTupleStruct(x, y)
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-1" id="example-javascript-usage-1">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
ExportedNamedStruct,
|
||
named_struct_by_value,
|
||
named_struct_by_shared_ref,
|
||
named_struct_by_exclusive_ref,
|
||
return_named_struct,
|
||
|
||
ExportedTupleStruct,
|
||
return_tuple_struct
|
||
} from './guide_supported_types_examples';
|
||
|
||
let namedStruct = return_named_struct(42);
|
||
console.log(namedStruct instanceof ExportedNamedStruct); // true
|
||
console.log(namedStruct.inner); // 42
|
||
|
||
named_struct_by_value(namedStruct);
|
||
named_struct_by_shared_ref(namedStruct);
|
||
named_struct_by_exclusive_ref(namedStruct);
|
||
|
||
let tupleStruct = return_tuple_struct(10, 20);
|
||
console.log(tupleStruct instanceof ExportedTupleStruct); // true
|
||
console.log(tupleStruct[0], tupleStruct[1]); // 10, 20
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#jsvalue" id="jsvalue"><code>JsValue</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Any JavaScript value</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-2" id="example-rust-usage-2">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_js_value_by_value(x: JsValue) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_js_value_by_shared_ref(x: &JsValue) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_js_value() -> JsValue {
|
||
JsValue::NULL
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-2" id="example-javascript-usage-2">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_js_value_by_value,
|
||
take_js_value_by_shared_ref,
|
||
return_js_value,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_js_value_by_value(42);
|
||
take_js_value_by_shared_ref('hello');
|
||
|
||
let v = return_js_value();
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#boxjsvalue" id="boxjsvalue"><code>Box<[JsValue]></code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">A JavaScript <code>Array</code> object</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-3" id="example-rust-usage-3">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_boxed_js_value_slice_by_value(x: Box<[JsValue]>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_boxed_js_value_slice() -> Box<[JsValue]> {
|
||
vec![JsValue::NULL, JsValue::UNDEFINED].into_boxed_slice()
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_option_boxed_js_value_slice(x: Option<Box<[JsValue]>>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_option_boxed_js_value_slice() -> Option<Box<[JsValue]>> {
|
||
None
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-3" id="example-javascript-usage-3">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_boxed_js_value_slice_by_value,
|
||
return_boxed_js_value_slice,
|
||
take_option_boxed_js_value_slice,
|
||
return_option_boxed_js_value_slice,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_boxed_js_value_slice_by_value([null, true, 2, {}, []]);
|
||
|
||
let values = return_boxed_js_value_slice();
|
||
console.log(values instanceof Array); // true
|
||
|
||
take_option_boxed_js_value_slice(null);
|
||
take_option_boxed_js_value_slice(undefined);
|
||
take_option_boxed_js_value_slice([1, 2, 3]);
|
||
|
||
let maybeValues = return_option_boxed_js_value_slice();
|
||
if (maybeValues == null) {
|
||
// ...
|
||
} else {
|
||
console.log(maybeValues instanceof Array); // true
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#const-t-and-mut-t" id="const-t-and-mut-t"><code>*const T</code> and <code>*mut T</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">A JavaScript number value</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-4" id="example-rust-usage-4">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use std::ptr;
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_pointer_by_value(x: *mut u8) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_pointer() -> *mut u8 {
|
||
ptr::null_mut()
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-4" id="example-javascript-usage-4">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_pointer_by_value,
|
||
return_pointer,
|
||
} from './guide_supported_types_examples';
|
||
import { memory } from './guide_supported_types_examples_bg';
|
||
|
||
let ptr = return_pointer();
|
||
let buf = new Uint8Array(memory.buffer);
|
||
let value = buf[ptr];
|
||
console.log(`The byte at the ${ptr} address is ${value}`);
|
||
|
||
take_pointer_by_value(ptr);
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#numbers-u8-i8-u16-i16-u32-i32-u64-i64-isize-usize-f32-and-f64" id="numbers-u8-i8-u16-i16-u32-i32-u64-i64-isize-usize-f32-and-f64">Numbers: <code>u8</code>, <code>i8</code>, <code>u16</code>, <code>i16</code>, <code>u32</code>, <code>i32</code>, <code>u64</code>, <code>i64</code>, <code>isize</code>, <code>usize</code>, <code>f32</code>, and <code>f64</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">A JavaScript number value</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-5" id="example-rust-usage-5">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_number_by_value(x: u32) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_number() -> f64 {
|
||
42.0
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_option_number(x: Option<u8>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_option_number() -> Option<i16> {
|
||
Some(-300)
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-5" id="example-javascript-usage-5">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_number_by_value,
|
||
return_number,
|
||
take_option_number,
|
||
return_option_number,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_number_by_value(42);
|
||
|
||
let x = return_number();
|
||
console.log(typeof x); // "number"
|
||
|
||
take_option_number(null);
|
||
take_option_number(undefined);
|
||
take_option_number(13);
|
||
|
||
let y = return_option_number();
|
||
if (y == null) {
|
||
// ...
|
||
} else {
|
||
console.log(typeof y); // "number"
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#bool" id="bool"><code>bool</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">A JavaScript boolean value</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-6" id="example-rust-usage-6">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_bool_by_value(x: bool) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_bool() -> bool {
|
||
true
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_option_bool(x: Option<bool>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_option_bool() -> Option<bool> {
|
||
Some(false)
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-6" id="example-javascript-usage-6">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_char_by_value,
|
||
return_char,
|
||
take_option_bool,
|
||
return_option_bool,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_bool_by_value(true);
|
||
|
||
let b = return_bool();
|
||
console.log(typeof b); // "boolean"
|
||
|
||
take_option_bool(null);
|
||
take_option_bool(undefined);
|
||
take_option_bool(true);
|
||
|
||
let c = return_option_bool();
|
||
if (c == null) {
|
||
// ...
|
||
} else {
|
||
console.log(typeof c); // "boolean"
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#char" id="char"><code>char</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">A JavaScript string value</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-7" id="example-rust-usage-7">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_char_by_value(x: char) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_char() -> char {
|
||
'🚀'
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-7" id="example-javascript-usage-7">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_char_by_value,
|
||
return_char,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_char_by_value('a');
|
||
|
||
let c = return_char();
|
||
console.log(typeof c); // "string"
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#str" id="str"><code>str</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">No</td><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">No</td><td align="center">No</td><td align="center">JavaScript string value</td></tr>
|
||
</tbody></table>
|
||
<p>Copies the string's contents back and forth between the JavaScript
|
||
garbage-collected heap and the Wasm linear memory with <code>TextDecoder</code> and
|
||
<code>TextEncoder</code>. If you don't want to perform this copy, and would rather work
|
||
with handles to JavaScript string values, use the <code>js_sys::JsString</code> type.</p>
|
||
<h2><a class="header" href="#example-rust-usage-8" id="example-rust-usage-8">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_str_by_shared_ref(x: &str) {}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-8" id="example-javascript-usage-8">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_str_by_shared_ref,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_str_by_shared_ref('hello');
|
||
|
||
</code></pre>
|
||
<h2><a class="header" href="#utf-16-vs-utf-8" id="utf-16-vs-utf-8">UTF-16 vs UTF-8</a></h2>
|
||
<p>Strings in JavaScript are encoded as UTF-16, but with one major exception: they
|
||
can contain unpaired surrogates. For some Unicode characters UTF-16 uses two
|
||
16-bit values. These are called "surrogate pairs" because they always come in
|
||
pairs. In JavaScript, it is possible for these surrogate pairs to be missing the
|
||
other half, creating an "unpaired surrogate".</p>
|
||
<p>When passing a string from JavaScript to Rust, it uses the <code>TextEncoder</code> API to
|
||
convert from UTF-16 to UTF-8. This is normally perfectly fine... unless there
|
||
are unpaired surrogates. In that case it will replace the unpaired surrogates
|
||
with U+FFFD (<28>, the replacement character). That means the string in Rust is
|
||
now different from the string in JavaScript!</p>
|
||
<p>If you want to guarantee that the Rust string is the same as the JavaScript
|
||
string, you should instead use <code>js_sys::JsString</code> (which keeps the string in
|
||
JavaScript and doesn't copy it into Rust).</p>
|
||
<p>If you want to access the raw value of a JS string, you can use <code>JsString::iter</code>,
|
||
which returns an <code>Iterator<Item = u16></code>. This perfectly preserves everything
|
||
(including unpaired surrogates), but it does not do any encoding (so you
|
||
have to do that yourself!).</p>
|
||
<p>If you simply want to ignore strings which contain unpaired surrogates, you can
|
||
use <code>JsString::is_valid_utf16</code> to test whether the string contains unpaired
|
||
surrogates or not.</p>
|
||
<h1><a class="header" href="#string" id="string"><code>String</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">JavaScript string value</td></tr>
|
||
</tbody></table>
|
||
<p>Copies the string's contents back and forth between the JavaScript
|
||
garbage-collected heap and the Wasm linear memory with <code>TextDecoder</code> and
|
||
<code>TextEncoder</code></p>
|
||
<blockquote>
|
||
<p><strong>Note</strong>: Be sure to check out the <a href="reference/types/str.html">documentation for <code>str</code></a> to
|
||
learn about some caveats when working with strings between JS and Rust.</p>
|
||
</blockquote>
|
||
<h2><a class="header" href="#example-rust-usage-9" id="example-rust-usage-9">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_string_by_value(x: String) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_string() -> String {
|
||
"hello".into()
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_option_string(x: Option<String>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_option_string() -> Option<String> {
|
||
None
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-9" id="example-javascript-usage-9">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_string_by_value,
|
||
return_string,
|
||
take_option_string,
|
||
return_option_string,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_string_by_value('hello');
|
||
|
||
let s = return_string();
|
||
console.log(typeof s); // "string"
|
||
|
||
take_option_string(null);
|
||
take_option_string(undefined);
|
||
take_option_string('hello');
|
||
|
||
let t = return_option_string();
|
||
if (t == null) {
|
||
// ...
|
||
} else {
|
||
console.log(typeof s); // "string"
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#number-slices-u8-i8-u16-i16-u32-i32-u64-i64-f32-and-f64" id="number-slices-u8-i8-u16-i16-u32-i32-u64-i64-f32-and-f64">Number Slices: <code>[u8]</code>, <code>[i8]</code>, <code>[u16]</code>, <code>[i16]</code>, <code>[u32]</code>, <code>[i32]</code>, <code>[u64]</code>, <code>[i64]</code>, <code>[f32]</code>, and <code>[f64]</code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<&T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">No</td><td align="center">A JavaScript <code>TypedArray</code> view of the Wasm memory for the boxed slice of the appropriate type (<code>Int32Array</code>, <code>Uint8Array</code>, etc)</td></tr>
|
||
</tbody></table>
|
||
<h2><a class="header" href="#example-rust-usage-10" id="example-rust-usage-10">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_number_slice_by_shared_ref(x: &[f64]) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_number_slice_by_exclusive_ref(x: &mut [u8]) {}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-10" id="example-javascript-usage-10">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_number_slice_by_shared_ref,
|
||
take_number_slice_by_exclusive_ref,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_number_slice_by_shared_ref(new Float64Array(100));
|
||
take_number_slice_by_exclusive_ref(new Uint8Array(100));
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#boxed-number-slices-boxu8-boxi8-boxu16-boxi16-boxu32-boxi32-boxu64-boxi64-boxf32-and-boxf64" id="boxed-number-slices-boxu8-boxi8-boxu16-boxi16-boxu32-boxi32-boxu64-boxi64-boxf32-and-boxf64">Boxed Number Slices: <code>Box<[u8]></code>, <code>Box<[i8]></code>, <code>Box<[u16]></code>, <code>Box<[i16]></code>, <code>Box<[u32]></code>, <code>Box<[i32]></code>, <code>Box<[u64]></code>, <code>Box<[i64]></code>, <code>Box<[f32]></code>, and <code>Box<[f64]></code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">Yes</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">A JavaScript <code>TypedArray</code> of the appropriate type (<code>Int32Array</code>, <code>Uint8Array</code>, etc...)</td></tr>
|
||
</tbody></table>
|
||
<p>Note that the contents of the slice are copied into the JavaScript <code>TypedArray</code>
|
||
from the Wasm linear memory when returning a boxed slice to JavaScript, and vice
|
||
versa when receiving a JavaScript <code>TypedArray</code> as a boxed slice in Rust.</p>
|
||
<h2><a class="header" href="#example-rust-usage-11" id="example-rust-usage-11">Example Rust Usage</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_boxed_number_slice_by_value(x: Box<[f64]>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_boxed_number_slice() -> Box<[u32]> {
|
||
(0..42).collect::<Vec<u32>>().into_boxed_slice()
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn take_option_boxed_number_slice(x: Option<Box<[u8]>>) {}
|
||
|
||
#[wasm_bindgen]
|
||
pub fn return_option_boxed_number_slice() -> Option<Box<[i32]>> {
|
||
None
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#example-javascript-usage-11" id="example-javascript-usage-11">Example JavaScript Usage</a></h2>
|
||
<pre><code class="language-js">import {
|
||
take_boxed_number_slice_by_value,
|
||
return_boxed_number_slice,
|
||
take_option_boxed_number_slice,
|
||
return_option_boxed_number_slice,
|
||
} from './guide_supported_types_examples';
|
||
|
||
take_boxed_number_slice_by_value(new Uint8Array(100));
|
||
|
||
let x = return_boxed_number_slice();
|
||
console.log(x instanceof Uint32Array); // true
|
||
|
||
take_option_boxed_number_slice(null);
|
||
take_option_boxed_number_slice(undefined);
|
||
take_option_boxed_number_slice(new Int16Array(256));
|
||
|
||
let y = return_option_boxed_number_slice();
|
||
if (y == null) {
|
||
// ...
|
||
} else {
|
||
console.log(x instanceof Int32Array); // true
|
||
}
|
||
|
||
</code></pre>
|
||
<h1><a class="header" href="#resultt-jsvalue" id="resultt-jsvalue"><code>Result<T, JsValue></code></a></h1>
|
||
<table><thead><tr><th align="center"><code>T</code> parameter</th><th align="center"><code>&T</code> parameter</th><th align="center"><code>&mut T</code> parameter</th><th align="center"><code>T</code> return value</th><th align="center"><code>Option<T></code> parameter</th><th align="center"><code>Option<T></code> return value</th><th align="center">JavaScript representation</th></tr></thead><tbody>
|
||
<tr><td align="center">No</td><td align="center">No</td><td align="center">No</td><td align="center">No</td><td align="center">No</td><td align="center">Yes</td><td align="center">Same as <code>T</code>, or an exception</td></tr>
|
||
</tbody></table>
|
||
<p>The <code>Result</code> type can be returned from functions exported to JS as well as
|
||
closures in Rust. Only <code>Result<T, JsValue></code> is supported where <code>T</code> can be
|
||
converted to JS. Whenever <code>Ok(val)</code> is encountered it's converted to JS and
|
||
handed off, and whenever <code>Err(error)</code> is encountered an exception is thrown in
|
||
JS with <code>error</code>.</p>
|
||
<p>You can use <code>Result</code> to enable handling of JS exceptions with <code>?</code> in Rust,
|
||
naturally propagating it upwards to the wasm boundary. Furthermore you can also
|
||
return custom types in Rust so long as they're all convertible to <code>JsValue</code>.</p>
|
||
<p>Note that if you import a JS function with <code>Result</code> you need
|
||
<code>#[wasm_bindgen(catch)]</code> to be annotated on the import (unlike exported
|
||
functions, which require no extra annotation). This may not be necessary in the
|
||
future though and it may work "as is"!.</p>
|
||
<h1><a class="header" href="#wasm_bindgen-attributes" id="wasm_bindgen-attributes"><code>#[wasm_bindgen]</code> Attributes</a></h1>
|
||
<p>The <code>#[wasm_bindgen]</code> 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!</p>
|
||
<h1><a class="header" href="#wasm_bindgen-on-javascript-imports" id="wasm_bindgen-on-javascript-imports"><code>#[wasm_bindgen]</code> on JavaScript Imports</a></h1>
|
||
<p>This section enumerates the attributes available for customizing bindings for
|
||
JavaScript functions and classes imported into Rust within an <code>extern "C" { ... }</code>
|
||
block.</p>
|
||
<h1><a class="header" href="#catch" id="catch"><code>catch</code></a></h1>
|
||
<p>The <code>catch</code> attribute allows catching a JavaScript exception. This can be
|
||
attached to any imported function or method, and the function must return a
|
||
<code>Result</code> where the <code>Err</code> payload is a <code>JsValue</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
// `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>;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>If calling the imported function throws an exception, then <code>Err</code> will be
|
||
returned with the exception that was raised. Otherwise, <code>Ok</code> is returned with
|
||
the result of the function.</p>
|
||
<blockquote>
|
||
<p>By default <code>wasm-bindgen</code> 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 <strong>will not execute destructors</strong>.
|
||
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!</p>
|
||
</blockquote>
|
||
<h1><a class="header" href="#constructor" id="constructor"><code>constructor</code></a></h1>
|
||
<p>The <code>constructor</code> attribute is used to indicate that the function being bound
|
||
should actually translate to calling the <code>new</code> 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:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Shoes;
|
||
|
||
#[wasm_bindgen(constructor)]
|
||
fn new() -> Shoes;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>This will attach a <code>new</code> static method to the <code>Shoes</code> type, and in JavaScript
|
||
when this method is called, it will be equivalent to <code>new Shoes()</code>.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// Become a cobbler; construct `new Shoes()`
|
||
let shoes = Shoes::new();
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#extends--class" id="extends--class"><code>extends = Class</code></a></h1>
|
||
<p>The <code>extends</code> attribute can be used to say that an imported type extends (in the
|
||
JS class hierarchy sense) another type. This will generate <code>AsRef</code>, <code>AsMut</code>, and
|
||
<code>From</code> impls for converting a type into another given that we statically know
|
||
the inheritance hierarchy:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Foo;
|
||
|
||
#[wasm_bindgen(extends = Foo)]
|
||
type Bar;
|
||
}
|
||
|
||
let x: &Bar = ...;
|
||
let y: &Foo = x.as_ref(); // zero cost cast
|
||
#}</code></pre></pre>
|
||
<p>The trait implementations generated for the above block are:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
impl From<Bar> for Foo { ... }
|
||
impl AsRef<Foo> for Bar { ... }
|
||
impl AsMut<Foo> for Bar { ... }
|
||
#}</code></pre></pre>
|
||
<p>The <code>extends = ...</code> attribute can be specified multiple times for longer
|
||
inheritance chains, and <code>AsRef</code> and such impls will be generated for each of
|
||
the types.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Foo;
|
||
|
||
#[wasm_bindgen(extends = Foo)]
|
||
type Bar;
|
||
|
||
#[wasm_bindgen(extends = Foo, extends = Bar)]
|
||
type Baz;
|
||
}
|
||
|
||
let x: &Baz = ...;
|
||
let y1: &Bar = x.as_ref();
|
||
let y2: &Foo = y1.as_ref();
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#getter-and-setter" id="getter-and-setter"><code>getter</code> and <code>setter</code></a></h1>
|
||
<p>These two attributes can be combined with <code>method</code> to indicate that this is a
|
||
getter or setter method. A <code>getter</code>-tagged function by default accesses the
|
||
JavaScript property with the same name as the getter function. A <code>setter</code>'s
|
||
function name is currently required to start with <code>set_</code> and the property it
|
||
accesses is the suffix after <code>set\_</code>.</p>
|
||
<p>Consider the following JavaScript class that has a getter and setter for the
|
||
<code>white_russians</code> property:</p>
|
||
<pre><code class="language-js">class TheDude {
|
||
get white_russians() {
|
||
...
|
||
}
|
||
set white_russians(val) {
|
||
...
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>We would import this with the following <code>#[wasm_bindgen]</code> attributes:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type TheDude;
|
||
|
||
#[wasm_bindgen(method, getter)]
|
||
fn white_russians(this: &TheDude) -> u32;
|
||
|
||
#[wasm_bindgen(method, setter)]
|
||
fn set_white_russians(this: &TheDude, val: u32);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here we're importing the <code>TheDude</code> type and defining the ability to access each
|
||
object's <code>white_russians</code> property. The first function here is a getter and will
|
||
be available in Rust as <code>the_dude.white_russians()</code>, and the latter is the
|
||
setter which is accessible as <code>the_dude.set_white_russians(2)</code>. Note that both
|
||
functions have a <code>this</code> argument as they're tagged with <code>method</code>.</p>
|
||
<p>Finally, you can also pass an argument to the <code>getter</code> and <code>setter</code>
|
||
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:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
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);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Heads up! <code>getter</code> and <code>setter</code> 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 <code>structural</code> attribute!</p>
|
||
<pre><code class="language-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;
|
||
};
|
||
</code></pre>
|
||
<h1><a class="header" href="#final" id="final"><code>final</code></a></h1>
|
||
<p>The <code>final</code> attribute is the converse of the <a href="reference/attributes/on-js-imports/structural.html"><code>structural</code>
|
||
attribute</a>. It configures how <code>wasm-bindgen</code> will generate JS
|
||
imports to call the imported function. Notably a function imported by <code>final</code>
|
||
never changes after it was imported, whereas a function imported by default (or
|
||
with <code>structural</code>) is subject to runtime lookup rules such as walking the
|
||
prototype chain of an object.</p>
|
||
<p>The <code>final</code> attribute is intended to be purely related to performance. It
|
||
ideally has no user-visible effect, and <code>structural</code> imports (the default)
|
||
should be able to transparently switch to <code>final</code> eventually.</p>
|
||
<p>The eventual performance aspect is that with the <a href="https://github.com/WebAssembly/host-bindings">host bindings
|
||
proposal</a> then <code>wasm-bindgen</code> will need to generate far fewer JS
|
||
function shims to import than it does today. For example, consider this import
|
||
today:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Foo;
|
||
#[wasm_bindgen(method)]
|
||
fn bar(this: &Foo, argument: &str) -> JsValue;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p><strong>Without the <code>final</code> attribute</strong> the generated JS looks like this:</p>
|
||
<pre><code class="language-js">// without `final`
|
||
export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) {
|
||
let varg1 = getStringFromWasm(arg1, arg2);
|
||
return addHeapObject(getObject(arg0).bar(varg1));
|
||
}
|
||
</code></pre>
|
||
<p>We can see here that this JS function shim is required, but it's all relatively
|
||
self-contained. It does, however, execute the <code>bar</code> method in a duck-type-y
|
||
fashion in the sense that it never validates <code>getObject(arg0)</code> is of type <code>Foo</code>
|
||
to actually call the <code>Foo.prototype.bar</code> method.</p>
|
||
<p>If we instead, however, write this:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Foo;
|
||
#[wasm_bindgen(method, final)] // note the change here
|
||
fn bar(this: &Foo, argument: &str) -> JsValue;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>it generates this JS glue (roughly):</p>
|
||
<pre><code class="language-js">const __wbg_bar_target = Foo.prototype.bar;
|
||
|
||
export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) {
|
||
let varg1 = getStringFromWasm(arg1, arg2);
|
||
return addHeapObject(__wbg_bar_target.call(getObject(arg0), varg1));
|
||
}
|
||
</code></pre>
|
||
<p>The difference here is pretty subtle, but we can see how the function being
|
||
called is hoisted out of the generated shim and is bound to always be
|
||
<code>Foo.prototype.bar</code>. This then uses the <code>Function.call</code> method to invoke that
|
||
function with <code>getObject(arg0)</code> as the receiver.</p>
|
||
<p>But wait, there's still a JS function shim here even with <code>final</code>! That's true,
|
||
and this is simply a fact of future WebAssembly proposals not being implemented
|
||
yet. The semantics, though, match the future <a href="https://github.com/WebAssembly/host-bindings">host bindings
|
||
proposal</a> because the method being called is determined exactly
|
||
once, and it's located on the prototype chain rather than being resolved at
|
||
runtime when the function is called.</p>
|
||
<h2><a class="header" href="#interaction-with-future-proposals" id="interaction-with-future-proposals">Interaction with future proposals</a></h2>
|
||
<p>If you're curious to see how our JS function shim will be eliminated entirely,
|
||
let's take a look at the generated bindings. We're starting off with this:</p>
|
||
<pre><code class="language-js">const __wbg_bar_target = Foo.prototype.bar;
|
||
|
||
export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) {
|
||
let varg1 = getStringFromWasm(arg1, arg2);
|
||
return addHeapObject(__wbg_bar_target.call(getObject(arg0), varg1));
|
||
}
|
||
</code></pre>
|
||
<p>... and once the <a href="https://github.com/WebAssembly/reference-types">reference types proposal</a> is implemented then
|
||
we won't need some of these pesky functions. That'll transform our generated JS
|
||
shim to look like:</p>
|
||
<pre><code class="language-js">const __wbg_bar_target = Foo.prototype.bar;
|
||
|
||
export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) {
|
||
let varg1 = getStringFromWasm(arg1, arg2);
|
||
return __wbg_bar_target.call(arg0, varg1);
|
||
}
|
||
</code></pre>
|
||
<p>Getting better! Next up we need the host bindings proposal. Note that the
|
||
proposal is undergoing some changes right now so it's tough to link to reference
|
||
documentation, but it suffices to say that it'll empower us with at least two
|
||
different features.</p>
|
||
<p>First, host bindings promises to provide the concept of "argument conversions".
|
||
The <code>arg1</code> and <code>arg2</code> values here are actually a pointer and a length to a utf-8
|
||
encoded string, and with host bindings we'll be able to annotate that this
|
||
import should take those two arguments and convert them to a JS string (that is,
|
||
the <em>host</em> should do this, the WebAssembly engine). Using that feature we can
|
||
futher trim this down to:</p>
|
||
<pre><code class="language-js">const __wbg_bar_target = Foo.prototype.bar;
|
||
|
||
export function __wbg_bar_a81456386e6b526f(arg0, varg1) {
|
||
return __wbg_bar_target.call(arg0, varg1);
|
||
}
|
||
</code></pre>
|
||
<p>And finally, the second promise of the host bindings proposal is that we can
|
||
flag a function call to indicate the first argument is the <code>this</code> binding of the
|
||
function call. Today the <code>this</code> value of all called imported functions is
|
||
<code>undefined</code>, and this flag (configured with host bindings) will indicate the
|
||
first argument here is actually the <code>this</code>.</p>
|
||
<p>With that in mind we can further transform this to:</p>
|
||
<pre><code class="language-js">export const __wbg_bar_a81456386e6b526f = Foo.prototype.bar;
|
||
</code></pre>
|
||
<p>and voila! We, with <a href="https://github.com/WebAssembly/reference-types">reference types</a> and <a href="https://github.com/WebAssembly/host-bindings">host
|
||
bindings</a>, now have no JS function shim at all necessary to call
|
||
the imported function. Additionally future wasm proposals to the ES module
|
||
system may also mean that don't even need the <code>export const ...</code> here too.</p>
|
||
<p>It's also worth pointing out that with all these wasm proposals implemented the
|
||
default way to import the <code>bar</code> function (aka <code>structural</code>) would generate a JS
|
||
function shim that looks like:</p>
|
||
<pre><code class="language-js">export function __wbg_bar_a81456386e6b526f(varg1) {
|
||
return this.bar(varg1);
|
||
}
|
||
</code></pre>
|
||
<p>where this import is still subject to runtime prototype chain lookups and such.</p>
|
||
<h1><a class="header" href="#indexing_getter-indexing_setter-and-indexing_deleter" id="indexing_getter-indexing_setter-and-indexing_deleter"><code>indexing_getter</code>, <code>indexing_setter</code>, and <code>indexing_deleter</code></a></h1>
|
||
<p>These three attributes indicate that a method is an dynamically intercepted
|
||
getter, setter, or deleter on the receiver object itself, rather than a direct
|
||
access of the receiver's properties. It is equivalent calling the Proxy handler
|
||
for the <code>obj[prop]</code> operation with some dynamic <code>prop</code> variable in JavaScript,
|
||
rather than a normal static property access like <code>obj.prop</code> on a normal
|
||
JavaScript <code>Object</code>.</p>
|
||
<p>This is useful for binding to <code>Proxy</code>s and some builtin DOM types that
|
||
dynamically intercept property accesses.</p>
|
||
<ul>
|
||
<li>
|
||
<p><code>indexing_getter</code> corresponds to <code>obj[prop]</code> operation in JavaScript. The
|
||
function annotated must have a <code>this</code> receiver parameter, a single parameter
|
||
that is used for indexing into the receiver (<code>prop</code>), and a return type.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>indexing_setter</code> corresponds to the <code>obj[prop] = val</code> operation in
|
||
JavaScript. The function annotated must have a <code>this</code> receiver parameter, a
|
||
parameter for indexing into the receiver (<code>prop</code>), and a value parameter
|
||
(<code>val</code>).</p>
|
||
</li>
|
||
<li>
|
||
<p><code>indexing_deleter</code> corresponds to <code>delete obj[prop]</code> operation in
|
||
JavaScript. The function annotated must have a <code>this</code> receiver and a single
|
||
parameter for indexing into the receiver (<code>prop</code>).</p>
|
||
</li>
|
||
</ul>
|
||
<p>These must always be used in conjunction with the <code>structural</code> and <code>method</code>
|
||
flags.</p>
|
||
<p>For example, consider this JavaScript snippet that uses <code>Proxy</code>:</p>
|
||
<pre><code class="language-js">const foo = new Proxy({}, {
|
||
get(obj, prop) {
|
||
return prop in obj ? obj[prop] : prop.length;
|
||
},
|
||
set(obj, prop, value) {
|
||
obj[prop] = value;
|
||
},
|
||
deleteProperty(obj, prop) {
|
||
delete obj[prop];
|
||
},
|
||
});
|
||
|
||
foo.ten;
|
||
// 3
|
||
|
||
foo.ten = 10;
|
||
foo.ten;
|
||
// 10
|
||
|
||
delete foo.ten;
|
||
foo.ten;
|
||
// 3
|
||
</code></pre>
|
||
<p>To bind that in <code>wasm-bindgen</code> in Rust, we would use the <code>indexing_*</code> attributes
|
||
on methods:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Foo;
|
||
static foo: 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);
|
||
}
|
||
|
||
assert_eq!(foo.get("ten"), 3);
|
||
|
||
foo.set("ten", 10);
|
||
assert_eq!(foo.get("ten"), 10);
|
||
|
||
foo.delete("ten");
|
||
assert_eq!(foo.get("ten"), 3);
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#js_class--blah" id="js_class--blah"><code>js_class = "Blah"</code></a></h1>
|
||
<p>The <code>js_class</code> attribute can be used in conjunction with the <code>method</code> attribute
|
||
to bind methods of imported JavaScript classes that have been renamed on the
|
||
Rust side.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
// 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;
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#js_name--blah" id="js_name--blah"><code>js_name = blah</code></a></h1>
|
||
<p>The <code>js_name</code> attribute can be used to bind to a different function in
|
||
JavaScript than the identifier that's defined in Rust.</p>
|
||
<p>Most often, this is used to convert a camel-cased JavaScript identifier into a
|
||
snake-cased Rust identifier:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_name = jsOftenUsesCamelCase)]
|
||
fn js_often_uses_camel_case() -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Sometimes, it is used to bind to JavaScript identifiers that are not valid Rust
|
||
identifiers, in which case <code>js_name = "some string"</code> is used instead of <code>js_name = ident</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_name = "$$$")]
|
||
fn cash_money() -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>However, you can also use <code>js_name</code> to define multiple signatures for
|
||
polymorphic JavaScript functions:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[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);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>All of these functions will call <code>console.log</code> in JavaScript, but each
|
||
identifier will have only one signature in Rust.</p>
|
||
<p>Note that if you use <code>js_name</code> when importing a type you'll also need to use the
|
||
<a href="reference/attributes/on-js-imports/js_class.html"><code>js_class</code> attribute</a> when defining methods on the type:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_name = String)]
|
||
type JsString;
|
||
#[wasm_bindgen(method, getter, js_class = "String")]
|
||
pub fn length(this: &JsString) -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#js_namespace--blah" id="js_namespace--blah"><code>js_namespace = blah</code></a></h1>
|
||
<p>This attribute indicates that the JavaScript type is accessed through the given
|
||
namespace. For example, the <code>WebAssembly.Module</code> APIs are all accessed through
|
||
the <code>WebAssembly</code> namespace. <code>js_namespace</code> 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.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
|
||
type Foo;
|
||
#[wasm_bindgen(constructor, js_namespace = Bar)]
|
||
fn new() -> Foo;
|
||
}
|
||
|
||
log("hello, console!");
|
||
Foo::new();
|
||
#}</code></pre></pre>
|
||
<p>This is an example of how to bind namespaced items in Rust. The <code>log</code> and <code>Foo::new</code> functions will
|
||
be available in the Rust module and will be invoked as <code>console.log</code> and <code>new Bar.Foo</code> in
|
||
JavaScript.</p>
|
||
<p>It is also possible to access the JavaScript object under the nested namespace.
|
||
<code>js_namespace</code> also accepts the array of the string to specify the namespace.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(js_namespace = ["window", "document"])]
|
||
fn write(s: &str);
|
||
}
|
||
|
||
write("hello, document!");
|
||
#}</code></pre></pre>
|
||
<p>This example shows how to bind <code>window.document.write</code> in Rust.</p>
|
||
<h1><a class="header" href="#method" id="method"><code>method</code></a></h1>
|
||
<p>The <code>method</code> attribute allows you to describe methods of imported JavaScript
|
||
objects. It is applied on a function that has <code>this</code> as its first parameter,
|
||
which is a shared reference to an imported JavaScript type.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Set;
|
||
|
||
#[wasm_bindgen(method)]
|
||
fn has(this: &Set, element: &JsValue) -> bool;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>This generates a <code>has</code> method on <code>Set</code> in Rust, which invokes the
|
||
<code>Set.prototype.has</code> method in JavaScript.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
let set: Set = ...;
|
||
let elem: JsValue = ...;
|
||
if set.has(&elem) {
|
||
...
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#module--blah" id="module--blah"><code>module = "blah"</code></a></h1>
|
||
<p>The <code>module</code> attributes configures the module from which items are imported. For
|
||
example,</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(module = "wu/tang/clan")]
|
||
extern "C" {
|
||
type ThirtySixChambers;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>generates JavaScript import glue like:</p>
|
||
<pre><code class="language-js">import { ThirtySixChambers } from "wu/tang/clan";
|
||
</code></pre>
|
||
<p>If a <code>module</code> attribute is not present, then the global scope is used
|
||
instead. For example,</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
fn illmatic() -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>generates JavaScript import glue like:</p>
|
||
<pre><code class="language-js">let illmatic = this.illmatic;
|
||
</code></pre>
|
||
<p>Note that if the string specified with <code>module</code> starts with <code>./</code>, <code>../</code>, or <code>/</code>
|
||
then it's interpreted as a path to a <a href="reference/attributes/on-js-imports/../../js-snippets.html">local JS snippet</a>.
|
||
If this doesn't work for your use case you might be interested in the
|
||
<a href="reference/attributes/on-js-imports/raw_module.html"><code>raw_module</code> attribute</a></p>
|
||
<h1><a class="header" href="#raw_module--blah" id="raw_module--blah"><code>raw_module = "blah"</code></a></h1>
|
||
<p>This attribute performs exactly the same purpose as the <a href="reference/attributes/on-js-imports/module.html"><code>module</code>
|
||
attribute</a> on JS imports, but it does not attempt to interpret
|
||
paths starting with <code>./</code>, <code>../</code>, or <code>/</code> as JS snippets. For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(raw_module = "./some/js/file.js")]
|
||
extern "C" {
|
||
fn the_function();
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Note that if you use this attribute with a relative or absolute path, it's
|
||
likely up to the final bundler or project to assign meaning to that path. This
|
||
typically means that the JS file or module will be resolved relative to the
|
||
final location of the wasm file itself. That means that <code>raw_module</code> is likely
|
||
unsuitable for libraries on crates.io, but may be usable within end-user
|
||
applications.</p>
|
||
<h1><a class="header" href="#static_method_of--blah" id="static_method_of--blah"><code>static_method_of = Blah</code></a></h1>
|
||
<p>The <code>static_method_of</code> 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 <code>Date.now()</code> static method, one would use this attribute:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Date;
|
||
|
||
#[wasm_bindgen(static_method_of = Date)]
|
||
pub fn now() -> f64;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>The <code>now</code> function becomes a static method of the imported type in the Rust
|
||
bindings as well:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
let instant = Date::now();
|
||
#}</code></pre></pre>
|
||
<p>This is similar to the <code>js_namespace</code> attribute, but the usage from within Rust
|
||
is different since the method also becomes a static method of the imported type.
|
||
Additionally this attribute also specifies that the <code>this</code> parameter when
|
||
invoking the method is expected to be the JS class, e.g. always invoked as
|
||
<code>Date.now()</code> instead of <code>const x = Date.now; x()</code>.</p>
|
||
<h1><a class="header" href="#structural" id="structural"><code>structural</code></a></h1>
|
||
<blockquote>
|
||
<p><strong>Note</strong>: As of <a href="https://rustwasm.github.io/rfcs/005-structural-and-deref.html">RFC 5</a> this attribute is the default for all imported
|
||
functions. This attribute is largely ignored today and is only retained for
|
||
backwards compatibility and learning purposes.</p>
|
||
<p>The inverse of this attribute, <a href="reference/attributes/on-js-imports/final.html">the <code>final</code>
|
||
attribute</a> is more functionally interesting than
|
||
<code>structural</code> (as <code>structural</code> is simply the default)</p>
|
||
</blockquote>
|
||
<p>The <code>structural</code> flag can be added to <code>method</code> 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.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
type Duck;
|
||
|
||
#[wasm_bindgen(method, structural)]
|
||
fn quack(this: &Duck);
|
||
|
||
#[wasm_bindgen(method, getter, structural)]
|
||
fn is_swimming(this: &Duck) -> bool;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>The constructor for the type here, <code>Duck</code>, is not required to exist in
|
||
JavaScript (it's not referenced). Instead <code>wasm-bindgen</code> will generate shims
|
||
that will access the passed in JavaScript value's <code>quack</code> method or its
|
||
<code>is_swimming</code> property.</p>
|
||
<pre><code class="language-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();
|
||
}
|
||
</code></pre>
|
||
<h1><a class="header" href="#variadic-parameters" id="variadic-parameters">Variadic Parameters</a></h1>
|
||
<p>In javascript, both the types of function arguments, and the number of function arguments are
|
||
dynamic. For example</p>
|
||
<pre><code class="language-js">function sum(...rest) {
|
||
let i;
|
||
// the old way
|
||
let old_way = 0;
|
||
for (i=0; i<arguments.length; i++) {
|
||
old_way += arguments[i];
|
||
}
|
||
// the new way
|
||
let new_way = 0;
|
||
for (i=0; i<rest.length; i++) {
|
||
new_way += rest[i];
|
||
}
|
||
// both give the same answer
|
||
assert(old_way === new_way);
|
||
return new_way;
|
||
}
|
||
</code></pre>
|
||
<p>This function doesn't translate directly into rust, since we don't currently support variadic
|
||
arguments on the wasm target. To bind to it, we use a slice as the last argument, and annotate the
|
||
function as variadic:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(variadic)]
|
||
fn sum(args: &[i32]) -> i32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>when we call this function, the last argument will be expanded as the javascript expects.</p>
|
||
<h1><a class="header" href="#vendor-prefixed-apis" id="vendor-prefixed-apis">Vendor-prefixed APIs</a></h1>
|
||
<p>On the web new APIs often have vendor prefixes while they're in an experimental
|
||
state. For example the <code>AudioContext</code> API is known as <code>webkitAudioContext</code> in
|
||
Safari at the time of this writing. The <code>vendor_prefix</code> attribute indicates
|
||
these alternative names, which are used if the normal name isn't defined.</p>
|
||
<p>For example to use <code>AudioContext</code> you might do:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(vendor_prefix = webkit)]
|
||
type AudioContext;
|
||
|
||
// methods on `AudioContext` ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Whenever <code>AudioContext</code> is used it'll use <code>AudioContext</code> if the global namespace
|
||
defines it or alternatively it'll fall back to <code>webkitAudioContext</code>.</p>
|
||
<p>Note that <code>vendor_prefix</code> cannot be used with <code>module = "..."</code> or
|
||
<code>js_namespace = ...</code>, so it's basically limited to web-platform APIs today.</p>
|
||
<h1><a class="header" href="#wasm_bindgen-on-rust-exports" id="wasm_bindgen-on-rust-exports"><code>#[wasm_bindgen]</code> on Rust Exports</a></h1>
|
||
<p>This section enumerates the attributes available for customizing bindings for
|
||
Rust functions and <code>struct</code>s exported to JavaScript.</p>
|
||
<h1><a class="header" href="#constructor-1" id="constructor-1"><code>constructor</code></a></h1>
|
||
<p>When attached to a Rust "constructor" it will make the generated JavaScript
|
||
bindings callable as <code>new Foo()</code>.</p>
|
||
<p>For example, consider this exported Rust type and <code>constructor</code> annotation:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[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
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>This can be used in JavaScript as:</p>
|
||
<pre><code class="language-js">import { Foo } from './my_module';
|
||
|
||
const f = new Foo();
|
||
console.log(f.get_contents());
|
||
</code></pre>
|
||
<h1><a class="header" href="#js_name--blah-1" id="js_name--blah-1"><code>js_name = Blah</code></a></h1>
|
||
<p>The <code>js_name</code> 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.</p>
|
||
<p>For example, this is often used to convert between Rust's snake-cased
|
||
identifiers into JavaScript's camel-cased identifiers:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(js_name = doTheThing)]
|
||
pub fn do_the_thing() -> u32 {
|
||
42
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>This can be used in JavaScript as:</p>
|
||
<pre><code class="language-js">import { doTheThing } from './my_module';
|
||
|
||
const x = doTheThing();
|
||
console.log(x);
|
||
</code></pre>
|
||
<p>Like imports, <code>js_name</code> can also be used to rename types exported to JS:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(js_name = Foo)]
|
||
pub struct JsFoo {
|
||
// ..
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>to be accessed like:</p>
|
||
<pre><code class="language-js">import { Foo } from './my_module';
|
||
|
||
// ...
|
||
</code></pre>
|
||
<p>Note that attaching methods to the JS class <code>Foo</code> should be done via the
|
||
<a href="reference/attributes/on-rust-exports/js_class.html"><code>js_class</code> attribute</a>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(js_name = Foo)]
|
||
pub struct JsFoo { /* ... */ }
|
||
|
||
#[wasm_bindgen(js_class = Foo)]
|
||
impl JsFoo {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#readonly" id="readonly"><code>readonly</code></a></h1>
|
||
<p>When attached to a <code>pub</code> struct field this indicates that it's read-only from
|
||
JavaScript, and a setter will not be generated and exported to JavaScript.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[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,
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here the <code>first</code> field will be both readable and writable from JS, but the
|
||
<code>second</code> field will be a <code>readonly</code> field in JS where the setter isn't
|
||
implemented and attempting to set it will throw an exception.</p>
|
||
<pre><code class="language-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);
|
||
</code></pre>
|
||
<h1><a class="header" href="#skip" id="skip"><code>skip</code></a></h1>
|
||
<p>When attached to a <code>pub</code> struct field this indicates that field will not be exposed to JavaScript,
|
||
and neither getter nor setter will be generated in ES6 class.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub struct Foo {
|
||
pub bar: u32,
|
||
|
||
#[wasm_bindgen(skip)]
|
||
pub baz: u32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl Foo {
|
||
pub fn new() -> Self {
|
||
Foo {
|
||
bar: 1,
|
||
baz: 2
|
||
}
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here the <code>bar</code> field will be both readable and writable from JS, but the
|
||
<code>baz</code> field will be <code>undefined</code> in JS.</p>
|
||
<pre><code class="language-js">import('./pkg/').then(rust => {
|
||
let foo = rust.Foo.new();
|
||
|
||
// bar is accessible by getter
|
||
console.log(foo.bar);
|
||
// field marked with `skip` is undefined
|
||
console.log(foo.baz);
|
||
|
||
// you can shadow it
|
||
foo.baz = 45;
|
||
// so accessing by getter will return `45`
|
||
// but it won't affect real value in rust memory
|
||
console.log(foo.baz);
|
||
});
|
||
</code></pre>
|
||
<h1><a class="header" href="#start" id="start"><code>start</code></a></h1>
|
||
<p>When attached to a <code>pub</code> function this attribute will configure the <code>start</code>
|
||
section of the wasm executable to be emitted, executing the tagged function as
|
||
soon as the wasm module is instantiated.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">#[wasm_bindgen(start)]
|
||
pub fn main() {
|
||
// executed automatically ...
|
||
}
|
||
</code></pre></pre>
|
||
<p>The <code>start</code> section of the wasm executable will be configured to execute the
|
||
<code>main</code> function here as soon as it can. Note that due to various practical
|
||
limitations today the start section of the executable may not literally point to
|
||
<code>main</code>, but the <code>main</code> function here should be started up automatically when the
|
||
wasm module is loaded.</p>
|
||
<p>There's a few caveats to be aware of when using the <code>start</code> attribute:</p>
|
||
<ul>
|
||
<li>The <code>start</code> function must take no arguments and must either return <code>()</code> or
|
||
<code>Result<(), JsValue></code></li>
|
||
<li>Only one <code>start</code> function can be placed into a module, including its
|
||
dependencies. If more than one is specified then <code>wasm-bindgen</code> will fail when
|
||
the CLI is run. It's recommended that only applications use this attribute.</li>
|
||
<li>The <code>start</code> function will not be executed when testing.</li>
|
||
<li>If you're experimenting with WebAssembly threads, the <code>start</code> function is
|
||
executed <em>once per thread</em>, not once globally!</li>
|
||
<li>Note that the <code>start</code> function is relatively new, so if you find any bugs with
|
||
it, please feel free to report an issue!</li>
|
||
</ul>
|
||
<h1><a class="header" href="#typescript_custom_section" id="typescript_custom_section"><code>typescript_custom_section</code></a></h1>
|
||
<p>When added to a <code>const</code> <code>&'static str</code>, it will append the contents of the
|
||
string to the <code>.d.ts</code> file exported by <code>wasm-bindgen-cli</code> (when the
|
||
<code>--typescript</code> flag is enabled).</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(typescript_custom_section)]
|
||
const TS_APPEND_CONTENT: &'static str = r#"
|
||
|
||
export type Coords = { "latitude": number, "longitude": number, };
|
||
|
||
"#;
|
||
#}</code></pre></pre>
|
||
<p>The primary target for this feature is for code generation. For example, you
|
||
can author a macro that allows you to export a TypeScript definition alongside
|
||
the definition of a struct or Rust type.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[derive(MyTypescriptExport)]
|
||
struct Coords {
|
||
latitude: u32,
|
||
longitude: u32,
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>The proc_derive_macro "MyTypescriptExport" can export its own
|
||
<code>#[wasm_bindgen(typescript_custom_section)]</code> section, which would then be
|
||
picked up by wasm-bindgen-cli. This would be equivalent to the contents of
|
||
the TS_APPEND_CONTENT string in the first example.</p>
|
||
<p>This feature allows plain data objects to be typechecked in Rust and in
|
||
TypeScript by outputing a type definition generated at compile time.</p>
|
||
<h1><a class="header" href="#getter-and-setter-1" id="getter-and-setter-1"><code>getter</code> and <code>setter</code></a></h1>
|
||
<p>The <code>getter</code> and <code>setter</code> attributes can be used in Rust <code>impl</code> blocks to define
|
||
properties in JS that act like getters and setters of a field. For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
pub struct Baz {
|
||
field: i32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl Baz {
|
||
#[wasm_bindgen(constructor)]
|
||
pub fn new(field: i32) -> Baz {
|
||
Baz { field }
|
||
}
|
||
|
||
#[wasm_bindgen(getter)]
|
||
pub fn field(&self) -> i32 {
|
||
self.field
|
||
}
|
||
|
||
#[wasm_bindgen(setter)]
|
||
pub fn set_field(&mut self, field: i32) {
|
||
self.field = field;
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Can be combined in <code>JavaScript</code> like in this snippet:</p>
|
||
<pre><code class="language-js">const obj = new Baz(3);
|
||
assert.equal(obj.field, 3);
|
||
obj.field = 4;
|
||
assert.equal(obj.field, 4);
|
||
</code></pre>
|
||
<p>You can also configure the name of the property that is exported in JS like so:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
impl Baz {
|
||
#[wasm_bindgen(getter = anotherName)]
|
||
pub fn field(&self) -> i32 {
|
||
self.field
|
||
}
|
||
|
||
#[wasm_bindgen(setter = anotherName)]
|
||
pub fn set_field(&mut self, field: i32) {
|
||
self.field = field;
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Getters are expected to take no arguments other than <code>&self</code> and return the
|
||
field's type. Setters are expected to take one argument other than <code>&mut self</code>
|
||
(or <code>&self</code>) and return no values.</p>
|
||
<p>The name for a <code>getter</code> is by default inferred from the function name it's
|
||
attached to. The default name for a <code>setter</code> is the function's name minus the
|
||
<code>set_</code> prefix, and if <code>set_</code> isn't a prefix of the function it's an error to not
|
||
provide the name explicitly.</p>
|
||
<h1><a class="header" href="#inspectable" id="inspectable"><code>inspectable</code></a></h1>
|
||
<p>By default, structs exported from Rust become JavaScript classes with a single <code>ptr</code> property. All other properties are implemented as getters, which are not displayed when calling <code>toJSON</code>.</p>
|
||
<p>The <code>inspectable</code> attribute can be used on Rust structs to provide a <code>toJSON</code> and <code>toString</code> implementation that display all readable fields. For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(inspectable)]
|
||
pub struct Baz {
|
||
pub field: i32,
|
||
private: i32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl Baz {
|
||
#[wasm_bindgen(constructor)]
|
||
pub fn new(field: i32) -> Baz {
|
||
Baz { field, private: 13 }
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Provides the following behavior as in this JavaScript snippet:</p>
|
||
<pre><code class="language-js">const obj = new Baz(3);
|
||
assert.deepStrictEqual(obj.toJSON(), { field: 3 });
|
||
obj.field = 4;
|
||
assert.strictEqual(obj.toString(), '{"field":4}');
|
||
</code></pre>
|
||
<p>One or both of these implementations can be overridden as desired. Note that the generated <code>toString</code> calls <code>toJSON</code> internally, so overriding <code>toJSON</code> will affect its output as a side effect.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
impl Baz {
|
||
#[wasm_bindgen(js_name = toJSON)]
|
||
pub fn to_json(&self) -> i32 {
|
||
self.field
|
||
}
|
||
|
||
#[wasm_bindgen(js_name = toString)]
|
||
pub fn to_string(&self) -> String {
|
||
format!("Baz: {}", self.field)
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Note that the output of <code>console.log</code> will remain unchanged and display only the <code>ptr</code> field in browsers. It is recommended to call <code>toJSON</code> or <code>JSON.stringify</code> in these situations to aid with logging or debugging. Node.js does not suffer from this limitation, see the section below.</p>
|
||
<h2><a class="header" href="#inspectable-classes-in-nodejs" id="inspectable-classes-in-nodejs"><code>inspectable</code> Classes in Node.js</a></h2>
|
||
<p>When the <code>nodejs</code> target is used, an additional <code>[util.inspect.custom]</code> implementation is provided which calls <code>toJSON</code> internally. This method is used for <code>console.log</code> and similar functions to display all readable fields of the Rust struct.</p>
|
||
<h1><a class="header" href="#skip_typescript" id="skip_typescript"><code>skip_typescript</code></a></h1>
|
||
<p>By default, Rust exports exposed to JavaScript will generate TypeScript definitions (unless <code>--no-typescript</code> is used). The <code>skip_typescript</code> attribute can be used to disable type generation per function, enum, struct, or field. For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(skip_typescript)]
|
||
pub enum MyHiddenEnum {
|
||
One,
|
||
Two,
|
||
Three
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
pub struct MyPoint {
|
||
pub x: u32,
|
||
|
||
#[wasm_bindgen(skip_typescript)]
|
||
pub y: u32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl MyPoint {
|
||
|
||
#[wasm_bindgen(skip_typescript)]
|
||
pub fn stringify(&self) -> String {
|
||
format!("({}, {})", self.x, self.y)
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Will generate the following <code>.d.ts</code> file:</p>
|
||
<pre><code class="language-ts">/* tslint:disable */
|
||
/* eslint-disable */
|
||
export class MyPoint {
|
||
free(): void;
|
||
x: number;
|
||
}
|
||
</code></pre>
|
||
<p>When combined with <a href="reference/attributes/on-rust-exports/typescript_custom_section.html">the <code>typescript_custom_section</code> attribute</a>, this can be used to manually specify more specific function types instead of using the generated definitions.</p>
|
||
<h1><a class="header" href="#typescript_type" id="typescript_type">typescript_type</a></h1>
|
||
<p>The <code>typescript_type</code> allows us to use typescript declarations in <code>typescript_custom_section</code> as arguments for rust functions! For example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(typescript_custom_section)]
|
||
const ITEXT_STYLE: &'static str = r#"
|
||
interface ITextStyle {
|
||
bold: boolean;
|
||
italic: boolean;
|
||
size: number;
|
||
}
|
||
"#;
|
||
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
#[wasm_bindgen(typescript_type = "ITextStyle")]
|
||
pub type ITextStyle;
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
#[derive(Default)]
|
||
pub struct TextStyle {
|
||
pub bold: bool,
|
||
pub italic: bool,
|
||
pub size: i32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl TextStyle {
|
||
#[wasm_bindgen(constructor)]
|
||
pub fn new(_i: ITextStyle) -> TextStyle {
|
||
// parse JsValue
|
||
TextStyle::default()
|
||
}
|
||
|
||
pub fn optional_new(_i: Option<ITextStyle>) -> TextStyle {
|
||
// parse JsValueo
|
||
TextStyle::default()
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>We can write our <code>typescript</code> code like: </p>
|
||
<pre><code class="language-ts">import { ITextStyle, TextStyle } from "./my_awesome_module";
|
||
|
||
const style: TextStyle = new TextStyle({
|
||
bold: true,
|
||
italic: true,
|
||
size: 42,
|
||
});
|
||
|
||
const optional_style: TextStyle = TextStyle.optional_new();
|
||
</code></pre>
|
||
<h1><a class="header" href="#the-web-sys-crate" id="the-web-sys-crate">The <code>web-sys</code> Crate</a></h1>
|
||
<p><a href="https://crates.io/crates/web-sys">The <code>web-sys</code> crate</a> provides raw <code>wasm-bindgen</code> imports for all of the Web's
|
||
APIs. This includes:</p>
|
||
<ul>
|
||
<li><code>window.fetch</code></li>
|
||
<li><code>Node.prototype.appendChild</code></li>
|
||
<li>WebGL</li>
|
||
<li>WebAudio</li>
|
||
<li>and many more!</li>
|
||
</ul>
|
||
<p>It's sort of like the <code>libc</code> crate, but for the Web.</p>
|
||
<p>It does <em>not</em> include the JavaScript APIs that are guaranteed to exist in all
|
||
standards-compliant ECMAScript environments, such as <code>Array</code>, <code>Date</code>, and
|
||
<code>eval</code>. Bindings for these APIs can be found in <a href="https://crates.io/crates/js-sys">the <code>js-sys</code> crate</a>.</p>
|
||
<h2><a class="header" href="#api-documentation" id="api-documentation">API Documentation</a></h2>
|
||
<p><a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/"><strong>Read the <code>web-sys</code> API documentation here!</strong></a></p>
|
||
<h1><a class="header" href="#using-web-sys" id="using-web-sys">Using <code>web-sys</code></a></h1>
|
||
<h2><a class="header" href="#add-web-sys-as-a-dependency-to-your-cargotoml" id="add-web-sys-as-a-dependency-to-your-cargotoml">Add <code>web-sys</code> as a dependency to your <code>Cargo.toml</code></a></h2>
|
||
<pre><code class="language-toml">[dependencies]
|
||
wasm-bindgen = "0.2"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3"
|
||
features = [
|
||
]
|
||
</code></pre>
|
||
<h2><a class="header" href="#enable-the-cargo-features-for-the-apis-youre-using" id="enable-the-cargo-features-for-the-apis-youre-using">Enable the cargo features for the APIs you're using</a></h2>
|
||
<p>To keep build times super speedy, <a href="web-sys/./cargo-features.html"><code>web-sys</code> gates each Web interface behind a
|
||
cargo feature</a>. Find the type or method you want to use
|
||
in the <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/">API documentation</a>; it will list the features that must be enabled
|
||
to access that API.</p>
|
||
<p>For example, if we're looking for <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/resizeTo">the <code>window.resizeTo</code>
|
||
function</a>, we would <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/?search=resizeTo">search for <code>resizeTo</code> in the API
|
||
documentation</a>. We would find <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.resize_to">the
|
||
<code>web_sys::Window::resize_to</code> function</a>, which requires the
|
||
<code>Window</code> feature. To get access to that function, we enable the <code>Window</code> feature
|
||
in <code>Cargo.toml</code>:</p>
|
||
<pre><code class="language-toml">[dependencies.web-sys]
|
||
version = "0.3"
|
||
features = [
|
||
"Window"
|
||
]
|
||
</code></pre>
|
||
<h2><a class="header" href="#call-the-method" id="call-the-method">Call the method!</a></h2>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
extern crate web_sys;
|
||
extern crate wasm_bindgen;
|
||
|
||
use wasm_bindgen::prelude::*;
|
||
use web_sys::Window;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn make_the_window_small() {
|
||
// Resize the window to 500px by 500px.
|
||
let window = web_sys::window().unwrap();
|
||
window.resize_to(500, 500)
|
||
.expect("could not resize the window");
|
||
}
|
||
#}</code></pre></pre>
|
||
<h1><a class="header" href="#cargo-features-in-web-sys" id="cargo-features-in-web-sys">Cargo Features in <code>web-sys</code></a></h1>
|
||
<p>To keep <code>web-sys</code> building as fast as possible, there is a cargo feature for
|
||
every type defined in <code>web-sys</code>. To access that type, you must enable its
|
||
feature. To access a method, you must enable the feature for its <code>self</code> type and
|
||
the features for each of its argument types. In the <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys">API documentation</a>, every
|
||
method lists the features that are required to enable it.</p>
|
||
<p>For example, <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.WebGlRenderingContext.html#method.compile_shader">the <code>WebGlRenderingContext::compile_shader</code> function</a> requires these features:</p>
|
||
<ul>
|
||
<li><code>WebGlRenderingContext</code>, because that is the method's <code>self</code> type</li>
|
||
<li><code>WebGlShader</code>, because it takes an argument of that type</li>
|
||
</ul>
|
||
<h1><a class="header" href="#function-overloads" id="function-overloads">Function Overloads</a></h1>
|
||
<p>Many Web APIs are overloaded to take different types of arguments or to skip
|
||
arguments completely. <code>web-sys</code> contains multiple bindings for these functions
|
||
that each specialize to a particular overload and set of argument types.</p>
|
||
<p>For example, <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">the <code>fetch</code> API</a> can be given a URL string, or a
|
||
<code>Request</code> object, and it might also optionally be given a <code>RequestInit</code> options
|
||
object. Therefore, we end up with these <code>web-sys</code> functions that all bind to the
|
||
<code>window.fetch</code> function:</p>
|
||
<ul>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str"><code>Window::fetch_with_str</code></a></li>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request"><code>Window::fetch_with_request</code></a></li>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str_and_init"><code>Window::fetch_with_str_and_init</code></a></li>
|
||
<li><a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request_and_init"><code>Window::fetch_with_request_and_init</code></a></li>
|
||
</ul>
|
||
<p>Note that different overloads can use different interfaces, and therefore can
|
||
require different sets of cargo features to be enabled.</p>
|
||
<h1><a class="header" href="#type-translations-in-web-sys" id="type-translations-in-web-sys">Type Translations in <code>web-sys</code></a></h1>
|
||
<p>Most of the types specified in <a href="https://heycam.github.io/webidl/">WebIDL (the interface definition language for
|
||
all Web APIs)</a> have relatively straightforward translations into
|
||
<code>web-sys</code>, but it's worth calling out a few in particular:</p>
|
||
<ul>
|
||
<li>
|
||
<p><code>BufferSource</code> and <code>ArrayBufferView</code> - these two types show up in a number of
|
||
APIs that generally deal with a buffer of bytes. We bind them in <code>web-sys</code>
|
||
with two different types, <code>js_sys::Object</code> and <code>&mut [u8]</code>. Using
|
||
<code>js_sys::Object</code> allows passing in arbitrary JS values which represent a view
|
||
of bytes (like any typed array object), and <code>&mut [u8]</code> allows using a raw
|
||
slice in Rust. Unfortunately we must pessimistically assume that JS will
|
||
modify all slices as we don't currently have information of whether they're
|
||
modified or not.</p>
|
||
</li>
|
||
<li>
|
||
<p>Callbacks are all represented as <code>js_sys::Function</code>. This means that all
|
||
callbacks going through <code>web-sys</code> are a raw JS value. You can work with this
|
||
by either juggling actual <code>js_sys::Function</code> instances or you can create a
|
||
<code>Closure<FnMut(...)></code>, extract the underlying <code>JsValue</code> with <code>as_ref</code>, and
|
||
then use <code>JsCast::unchecked_ref</code> to convert it to a <code>js_sys::Function</code>.</p>
|
||
</li>
|
||
</ul>
|
||
<h1><a class="header" href="#inheritance-in-web-sys" id="inheritance-in-web-sys">Inheritance in <code>web-sys</code></a></h1>
|
||
<p>Inheritance between JS classes is the bread and butter of how the DOM works on
|
||
the web, and as a result it's quite important for <code>web-sys</code> to provide access to
|
||
this inheritance hierarchy as well! There are few ways you can access the
|
||
inheritance hierarchy when using <code>web-sys</code>.</p>
|
||
<h3><a class="header" href="#accessing-parent-classes-using-deref" id="accessing-parent-classes-using-deref">Accessing parent classes using <code>Deref</code></a></h3>
|
||
<p>Like smart pointers in Rust, all types in <code>web_sys</code> implement <code>Deref</code> to their
|
||
parent JS class. This means, for example, if you have a <code>web_sys::Element</code> you
|
||
can create a <code>web_sys::Node</code> from that implicitly:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
let element: &Element = ...;
|
||
|
||
element.append_child(..); // call a method on `Node`
|
||
|
||
method_expecting_a_node(&element); // coerce to `&Node` implicitly
|
||
|
||
let node: &Node = &element; // explicitly coerce to `&Node`
|
||
#}</code></pre></pre>
|
||
<p>Using <code>Deref</code> allows ergonomic transitioning up the inheritance hierarchy to the
|
||
parent class and beyond, giving you access to all the methods using the <code>.</code>
|
||
operator.</p>
|
||
<h3><a class="header" href="#accessing-parent-classes-using-asref" id="accessing-parent-classes-using-asref">Accessing parent classes using <code>AsRef</code></a></h3>
|
||
<p>In addition to <code>Deref</code>, the <code>AsRef</code> trait is implemented for all types in
|
||
<code>web_sys</code> for all types in the inheritance hierarchy. For example for the
|
||
<code>HtmlAnchorElement</code> type you'll find:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
impl AsRef<HtmlElement> for HtmlAnchorElement
|
||
impl AsRef<Element> for HtmlAnchorElement
|
||
impl AsRef<Node> for HtmlAnchorElement
|
||
impl AsRef<EventTarget> for HtmlAnchorElement
|
||
impl AsRef<Object> for HtmlAnchorElement
|
||
impl AsRef<JsValue> for HtmlAnchorElement
|
||
#}</code></pre></pre>
|
||
<p>You can use <code>.as_ref()</code> to explicitly get a reference to any parent class from
|
||
from a type in <code>web_sys</code>. Note that because of the number of <code>AsRef</code>
|
||
implementations you'll likely need to have type inference guidance as well.</p>
|
||
<h3><a class="header" href="#accessing-child-clases-using-jscast" id="accessing-child-clases-using-jscast">Accessing child clases using <code>JsCast</code></a></h3>
|
||
<p>Finally the <code>wasm_bindgen::JsCast</code> trait can be used to implement all manner of
|
||
casts between types. It supports static unchecked casts between types as well as
|
||
dynamic runtime-checked casts (using <code>instanceof</code>) between types.</p>
|
||
<p>More documentation about this can be found <a href="https://docs.rs/wasm-bindgen/0.2/wasm_bindgen/trait.JsCast.html">on the trait itself</a></p>
|
||
<h1><a class="header" href="#unstable-apis" id="unstable-apis">Unstable APIs</a></h1>
|
||
<p>It's common for browsers to implement parts of a web API while the specification
|
||
for that API is still being written. The API may require frequent changes as the
|
||
specification continues to be developed, so the WebIDL is relatively unstable.</p>
|
||
<p>This causes some challenges for <code>web-sys</code> because it means <code>web-sys</code> would have
|
||
to make breaking API changes whenever the WebIDL changes. It also means that
|
||
previously published <code>web-sys</code> versions would be invalid, because the browser
|
||
API may have been changed to match the updated WebIDL.</p>
|
||
<p>To avoid frequent breaking changes for unstable APIs, <code>web-sys</code> hides all
|
||
unstable APIs through an attribute that looks like:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[cfg(web_sys_unstable_apis)]
|
||
pub struct Foo;
|
||
#}</code></pre></pre>
|
||
<p>By hiding unstable APIs through an attribute, it's necessary for crates to
|
||
explicitly opt-in to these reduced stability guarantees in order to use these
|
||
APIs. Specifically, these APIs do not follow semver and may break whenever the
|
||
WebIDL changes.</p>
|
||
<p>Crates can opt-in to unstable APIs at compile-time by passing the <code>cfg</code> flag
|
||
<code>web_sys_unstable_apis</code>. Typically the <code>RUSTFLAGS</code> environment variable is used
|
||
to do this. For example:</p>
|
||
<pre><code class="language-bash">RUSTFLAGS=--cfg=web_sys_unstable_apis cargo run
|
||
</code></pre>
|
||
<h1><a class="header" href="#testing-on-wasm32-unknown-unknown-with-wasm-bindgen-test" id="testing-on-wasm32-unknown-unknown-with-wasm-bindgen-test">Testing on <code>wasm32-unknown-unknown</code> with <code>wasm-bindgen-test</code></a></h1>
|
||
<p>The <code>wasm-bindgen-test</code> crate is an experimental test harness for Rust programs
|
||
compiled to wasm using <code>wasm-bindgen</code> and the <code>wasm32-unknown-unknown</code>
|
||
target.</p>
|
||
<h2><a class="header" href="#goals" id="goals">Goals</a></h2>
|
||
<ul>
|
||
<li>
|
||
<p>Write tests for wasm as similar as possible to how you normally would write
|
||
<code>#[test]</code>-style unit tests for native targets.</p>
|
||
</li>
|
||
<li>
|
||
<p>Run the tests with the usual <code>cargo test</code> command but with an explicit wasm
|
||
target:</p>
|
||
<pre><code>cargo test --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
</li>
|
||
</ul>
|
||
<h1><a class="header" href="#using-wasm-bindgen-test" id="using-wasm-bindgen-test">Using <code>wasm-bindgen-test</code></a></h1>
|
||
<h3><a class="header" href="#add-wasm-bindgen-test-to-your-cargotomls-dev-dependencies" id="add-wasm-bindgen-test-to-your-cargotomls-dev-dependencies">Add <code>wasm-bindgen-test</code> to Your <code>Cargo.toml</code>'s <code>[dev-dependencies]</code></a></h3>
|
||
<pre><code class="language-toml">[dev-dependencies]
|
||
wasm-bindgen-test = "0.3.0"
|
||
</code></pre>
|
||
<p>Note that the <code>0.3.0</code> track of <code>wasm-bindgen-test</code> supports Rust 1.39.0+, which
|
||
is currently the nightly channel (as of 2019-09-05). If you want support for
|
||
older compilers use the <code>0.2.*</code> track of <code>wasm-bindgen-test</code>.</p>
|
||
<h2><a class="header" href="#write-some-tests" id="write-some-tests">Write Some Tests</a></h2>
|
||
<p>Create a <code>$MY_CRATE/tests/wasm.rs</code> file:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
extern crate wasm_bindgen_test;
|
||
use wasm_bindgen_test::*;
|
||
|
||
#[wasm_bindgen_test]
|
||
fn pass() {
|
||
assert_eq!(1, 1);
|
||
}
|
||
|
||
#[wasm_bindgen_test]
|
||
fn fail() {
|
||
assert_eq!(1, 2);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Writing tests is the same as normal Rust <code>#[test]</code>s, except we are using the
|
||
<code>#[wasm_bindgen_test]</code> attribute.</p>
|
||
<p>One other difference is that the tests <strong>must</strong> be in the root of the crate, or
|
||
within a <code>pub mod</code>. Putting them inside a private module will not work.</p>
|
||
<h2><a class="header" href="#execute-your-tests" id="execute-your-tests">Execute Your Tests</a></h2>
|
||
<p>Run the tests with <code>wasm-pack test</code>. By default, the tests are generated to
|
||
target Node.js, but you can <a href="wasm-bindgen-test/./browsers.html">configure tests to run inside headless
|
||
browsers</a> as well.</p>
|
||
<pre><code class="language-shell">$ wasm-pack test --node
|
||
Finished dev [unoptimized + debuginfo] target(s) in 0.11s
|
||
Running /home/.../target/wasm32-unknown-unknown/debug/deps/wasm-4a309ffe6ad80503.wasm
|
||
running 2 tests
|
||
|
||
test wasm::pass ... ok
|
||
test wasm::fail ... FAILED
|
||
|
||
failures:
|
||
|
||
---- wasm::fail output ----
|
||
error output:
|
||
panicked at 'assertion failed: `(left == right)`
|
||
left: `1`,
|
||
right: `2`', crates/test/tests/wasm.rs:14:5
|
||
|
||
JS exception that was thrown:
|
||
RuntimeError: unreachable
|
||
at __rust_start_panic (wasm-function[1362]:33)
|
||
at rust_panic (wasm-function[1357]:30)
|
||
at std::panicking::rust_panic_with_hook::h56e5e464b0e7fc22 (wasm-function[1352]:444)
|
||
at std::panicking::continue_panic_fmt::had70ba48785b9a8f (wasm-function[1350]:122)
|
||
at std::panicking::begin_panic_fmt::h991e7d1ca9bf9c0c (wasm-function[1351]:95)
|
||
at wasm::fail::ha4c23c69dfa0eea9 (wasm-function[88]:477)
|
||
at core::ops::function::FnOnce::call_once::h633718dad359559a (wasm-function[21]:22)
|
||
at wasm_bindgen_test::__rt::Context::execute::h2f669104986475eb (wasm-function[13]:291)
|
||
at __wbg_test_fail_1 (wasm-function[87]:57)
|
||
at module.exports.__wbg_apply_2ba774592c5223a7 (/home/alex/code/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/wasm-4a309ffe6ad80503.js:61:66)
|
||
|
||
|
||
failures:
|
||
|
||
wasm::fail
|
||
|
||
test result: FAILED. 1 passed; 1 failed; 0 ignored
|
||
|
||
error: test failed, to rerun pass '--test wasm'
|
||
</code></pre>
|
||
<p>That's it!</p>
|
||
<hr />
|
||
<h2><a class="header" href="#appendix-using-wasm-bindgen-test-without-wasm-pack" id="appendix-using-wasm-bindgen-test-without-wasm-pack">Appendix: Using <code>wasm-bindgen-test</code> without <code>wasm-pack</code></a></h2>
|
||
<p><strong>⚠️ The recommended way to use <code>wasm-bindgen-test</code> is with <code>wasm-pack</code>, since it
|
||
will handle installing the test runner, installing a WebDriver client for your
|
||
browser, and informing <code>cargo</code> how to use the custom test runner.</strong> However, you
|
||
can also manage those tasks yourself, if you wish.</p>
|
||
<p>In addition to the steps above, you must also do the following.</p>
|
||
<h3><a class="header" href="#install-the-test-runner" id="install-the-test-runner">Install the Test Runner</a></h3>
|
||
<p>The test runner comes along with the main <code>wasm-bindgen</code> CLI tool. Make sure to
|
||
replace "X.Y.Z" with the same version of <code>wasm-bindgen</code> that you already have in
|
||
<code>Cargo.toml</code>!</p>
|
||
<pre><code class="language-shell">cargo install wasm-bindgen-cli --vers "X.Y.Z"
|
||
</code></pre>
|
||
<h3><a class="header" href="#configure-cargoconfig-to-use-the-test-runner" id="configure-cargoconfig-to-use-the-test-runner">Configure <code>.cargo/config</code> to use the Test Runner</a></h3>
|
||
<p>Add this to <code>$MY_CRATE/.cargo/config</code>:</p>
|
||
<pre><code class="language-toml">[target.wasm32-unknown-unknown]
|
||
runner = 'wasm-bindgen-test-runner'
|
||
</code></pre>
|
||
<h3><a class="header" href="#run-the-tests" id="run-the-tests">Run the Tests</a></h3>
|
||
<p>Run the tests by passing <code>--target wasm32-unknown-unknown</code> to <code>cargo test</code>:</p>
|
||
<pre><code>cargo test --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
<h1><a class="header" href="#writing-asynchronous-tests" id="writing-asynchronous-tests">Writing Asynchronous Tests</a></h1>
|
||
<p>Not all tests can execute immediately and some may need to do "blocking" work
|
||
like fetching resources and/or other bits and pieces. To accommodate this
|
||
asynchronous tests are also supported through the <code>futures</code> and
|
||
<code>wasm-bindgen-futures</code> crates.</p>
|
||
<p>Writing an asynchronous test is pretty simple, just use an <code>async</code> function!
|
||
You'll also likely want to use the <code>wasm-bindgen-futures</code> crate to convert JS
|
||
promises to Rust futures.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen::prelude::*;
|
||
use wasm_bindgen_futures::JsFuture;
|
||
|
||
#[wasm_bindgen_test]
|
||
async fn my_async_test() {
|
||
// Create a promise that is ready on the next tick of the micro task queue.
|
||
let promise = js_sys::Promise::resolve(&JsValue::from(42));
|
||
|
||
// Convert that promise into a future and make the test wait on it.
|
||
let x = JsFuture::from(promise).await.unwrap();
|
||
assert_eq!(x, 42);
|
||
}
|
||
#}</code></pre></pre>
|
||
<h2><a class="header" href="#rust-compiler-compatibility" id="rust-compiler-compatibility">Rust compiler compatibility</a></h2>
|
||
<p>Note that <code>async</code> functions are only supported in stable from Rust 1.39.0 and
|
||
beyond. As of the time of this writing (2019-09-05) this is the Nightly channel
|
||
of Rust.</p>
|
||
<p>If you're using the <code>futures</code> crate from crates.io in its 0.1 version then
|
||
you'll want to use the <code>0.3.*</code> version of <code>wasm-bindgen-futures</code> and the <code>0.2.8</code>
|
||
version of <code>wasm-bindgen-test</code>. In those modes you'll also need to use
|
||
<code>#[wasm_bindgen_test(async)]</code> instead of using an <code>async</code> function. In general
|
||
we'd recommend using the nightly version with <code>async</code> since the user experience
|
||
is much improved!</p>
|
||
<h1><a class="header" href="#testing-in-headless-browsers" id="testing-in-headless-browsers">Testing in Headless Browsers</a></h1>
|
||
<h2><a class="header" href="#configure-your-test-crate" id="configure-your-test-crate">Configure Your Test Crate</a></h2>
|
||
<p>Add this to the root of your test crate, e.g. <code>$MY_CRATE/tests/web.rs</code>:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||
|
||
wasm_bindgen_test_configure!(run_in_browser);
|
||
#}</code></pre></pre>
|
||
<p>Note that although a particular test crate must target either headless browsers
|
||
or Node.js, you can have test suites for both Node.js and browsers for your
|
||
project by using multiple test crates. For example:</p>
|
||
<pre><code>$MY_CRATE/
|
||
`-- tests
|
||
|-- node.rs # The tests in this suite use the default Node.js.
|
||
`-- web.rs # The tests in this suite are configured for browsers.
|
||
</code></pre>
|
||
<h2><a class="header" href="#configuring-which-browser-is-used" id="configuring-which-browser-is-used">Configuring Which Browser is Used</a></h2>
|
||
<p>To control which browser is used for headless testing, use the appropriate flag
|
||
with <code>wasm-pack test</code>:</p>
|
||
<ul>
|
||
<li>
|
||
<p><code>wasm-pack test --chrome</code> — Run the tests in Chrome. This machine must
|
||
have Chrome installed.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>wasm-pack test --firefox</code> — Run the tests in Firefox. This machine must
|
||
have Firefox installed.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>wasm-pack test --safari</code> — Run the tests in Safari. This machine must
|
||
have Safari installed.</p>
|
||
</li>
|
||
</ul>
|
||
<p>If multiple browser flags are passed, the tests will be run under each browser.</p>
|
||
<h2><a class="header" href="#running-the-tests-in-the-headless-browser" id="running-the-tests-in-the-headless-browser">Running the Tests in the Headless Browser</a></h2>
|
||
<p>Once the tests are configured to run in a headless browser, just run <code>wasm-pack test</code> with the appropriate browser flags and <code>--headless</code>:</p>
|
||
<pre><code class="language-bash">wasm-pack test --headless --chrome --firefox --safari
|
||
</code></pre>
|
||
<h2><a class="header" href="#configuring-headless-browser-capabilities" id="configuring-headless-browser-capabilities">Configuring Headless Browser capabilities</a></h2>
|
||
<p>Add the file <code>webdriver.json</code> to the root of your crate. Each browser has own
|
||
section for capabilities. For example:</p>
|
||
<pre><code class="language-json">{
|
||
"moz:firefoxOptions": {
|
||
"prefs": {
|
||
"media.navigator.streams.fake": true,
|
||
"media.navigator.permission.disabled": true
|
||
},
|
||
"args": []
|
||
},
|
||
"goog:chromeOptions": {
|
||
"args": [
|
||
"--use-fake-device-for-media-stream",
|
||
"--use-fake-ui-for-media-stream"
|
||
]
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>Full list supported capabilities can be found:</p>
|
||
<ul>
|
||
<li>for Chrome - <a href="https://peter.sh/experiments/chromium-command-line-switches/">here</a></li>
|
||
<li>for Firefox - <a href="https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions">here</a></li>
|
||
</ul>
|
||
<p>Note that the <code>headless</code> argument is always enabled for both browsers.</p>
|
||
<h3><a class="header" href="#debugging-headless-browser-tests" id="debugging-headless-browser-tests">Debugging Headless Browser Tests</a></h3>
|
||
<p>Omitting the <code>--headless</code> flag will disable headless mode, and allow you to
|
||
debug failing tests in your browser's devtools.</p>
|
||
<hr />
|
||
<h2><a class="header" href="#appendix-testing-in-headless-browsers-without-wasm-pack" id="appendix-testing-in-headless-browsers-without-wasm-pack">Appendix: Testing in headless browsers without <code>wasm-pack</code></a></h2>
|
||
<p><strong>⚠️ The recommended way to use <code>wasm-bindgen-test</code> is with <code>wasm-pack</code>, since it
|
||
will handle installing the test runner, installing a WebDriver client for your
|
||
browser, and informing <code>cargo</code> how to use the custom test runner.</strong> However, you
|
||
can also manage those tasks yourself, if you wish.</p>
|
||
<h3><a class="header" href="#configuring-which-browser-is-used-1" id="configuring-which-browser-is-used-1">Configuring Which Browser is Used</a></h3>
|
||
<p>If one of the following environment variables is set, then the corresponding
|
||
WebDriver and browser will be used. If none of these environment variables are
|
||
set, then the <code>$PATH</code> is searched for a suitable WebDriver implementation.</p>
|
||
<h4><a class="header" href="#geckodriverpathtogeckodriver" id="geckodriverpathtogeckodriver"><code>GECKODRIVER=path/to/geckodriver</code></a></h4>
|
||
<p>Use Firefox for headless browser testing, and <code>geckodriver</code> as its
|
||
WebDriver.</p>
|
||
<p>The <code>firefox</code> binary must be on your <code>$PATH</code>.</p>
|
||
<p><a href="https://github.com/mozilla/geckodriver/releases">Get <code>geckodriver</code> here</a></p>
|
||
<h4><a class="header" href="#chromedriverpathtochromedriver" id="chromedriverpathtochromedriver"><code>CHROMEDRIVER=path/to/chromedriver</code></a></h4>
|
||
<p>Use Chrome for headless browser testing, and <code>chromedriver</code> as its
|
||
WebDriver.</p>
|
||
<p>The <code>chrome</code> binary must be on your <code>$PATH</code>.</p>
|
||
<p><a href="http://chromedriver.chromium.org/downloads">Get <code>chromedriver</code> here</a></p>
|
||
<h4><a class="header" href="#safaridriverpathtosafaridriver" id="safaridriverpathtosafaridriver"><code>SAFARIDRIVER=path/to/safaridriver</code></a></h4>
|
||
<p>Use Safari for headless browser testing, and <code>safaridriver</code> as its
|
||
WebDriver.</p>
|
||
<p>This is installed by default on Mac OS. It should be able to find your Safari
|
||
installation by default.</p>
|
||
<h3><a class="header" href="#running-the-tests-in-the-remote-headless-browser" id="running-the-tests-in-the-remote-headless-browser">Running the Tests in the Remote Headless Browser</a></h3>
|
||
<p>Tests can be run on a remote webdriver. To do this, the above environment
|
||
variables must be set as URL to the remote webdriver. For example:</p>
|
||
<pre><code>CHROMEDRIVER_REMOTE=http://remote.host/
|
||
</code></pre>
|
||
<h3><a class="header" href="#running-the-tests-in-the-headless-browser-1" id="running-the-tests-in-the-headless-browser-1">Running the Tests in the Headless Browser</a></h3>
|
||
<p>Once the tests are configured to run in a headless browser and the appropriate
|
||
environment variables are set, executing the tests for headless browsers is the
|
||
same as executing them for Node.js:</p>
|
||
<pre><code class="language-bash">cargo test --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
<h4><a class="header" href="#debugging-headless-browser-tests-1" id="debugging-headless-browser-tests-1">Debugging Headless Browser Tests</a></h4>
|
||
<p>Set the <code>NO_HEADLESS=1</code> environment variable and the browser tests will not run
|
||
headless. Instead, the tests will start a local server that you can visit in
|
||
your Web browser of choices, and headless testing should not be used. You can
|
||
then use your browser's devtools to debug.</p>
|
||
<h1><a class="header" href="#setting-up-continuous-integration-with-wasm-bindgen-test" id="setting-up-continuous-integration-with-wasm-bindgen-test">Setting Up Continuous Integration with <code>wasm-bindgen-test</code></a></h1>
|
||
<p>This page contains example configurations for running <code>wasm-bindgen-test</code>-based
|
||
tests in various CI services.</p>
|
||
<p>Is your favorite CI service missing? <a href="https://github.com/rustwasm/wasm-bindgen">Send us a pull
|
||
request!</a></p>
|
||
<h2><a class="header" href="#travis-ci" id="travis-ci">Travis CI</a></h2>
|
||
<pre><code class="language-yaml">language: rust
|
||
rust : nightly
|
||
|
||
addons:
|
||
firefox: latest
|
||
chrome : stable
|
||
|
||
install:
|
||
- curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||
|
||
script:
|
||
|
||
# this will test the non wasm targets if your crate has those, otherwise remove this line.
|
||
#
|
||
- cargo test
|
||
|
||
- wasm-pack test --firefox --headless
|
||
- wasm-pack test --chrome --headless
|
||
</code></pre>
|
||
<h2><a class="header" href="#appveyor" id="appveyor">AppVeyor</a></h2>
|
||
<pre><code class="language-yaml">install:
|
||
- ps: Install-Product node 10
|
||
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||
- rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
|
||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||
- rustc -V
|
||
- cargo -V
|
||
- rustup target add wasm32-unknown-unknown
|
||
- cargo install wasm-bindgen-cli
|
||
|
||
build: false
|
||
|
||
test_script:
|
||
# Test in Chrome. chromedriver is installed by default in appveyor.
|
||
- set CHROMEDRIVER=C:\Tools\WebDriver\chromedriver.exe
|
||
- cargo test --target wasm32-unknown-unknown
|
||
- set CHROMEDRIVER=
|
||
# Test in Firefox. geckodriver is also installed by default.
|
||
- set GECKODRIVER=C:\Tools\WebDriver\geckodriver.exe
|
||
- cargo test --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
<h2><a class="header" href="#github-actions" id="github-actions">GitHub Actions</a></h2>
|
||
<pre><code class="language-yaml">on: [push, pull_request]
|
||
|
||
jobs:
|
||
test:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v2
|
||
|
||
- name: Install
|
||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||
|
||
- run: cargo test
|
||
- run: wasm-pack test --headless --chrome
|
||
- run: wasm-pack test --headless --firefox
|
||
</code></pre>
|
||
<h1><a class="header" href="#contributing-to-wasm-bindgen" id="contributing-to-wasm-bindgen">Contributing to <code>wasm-bindgen</code></a></h1>
|
||
<p>This section contains instructions on how to get this project up and running for
|
||
development. You may want to browse the [unpublished guide documentation] for
|
||
<code>wasm-bindgen</code> as well as it may have more up-to-date information.</p>
|
||
<h2><a class="header" href="#prerequisites" id="prerequisites">Prerequisites</a></h2>
|
||
<ol>
|
||
<li>
|
||
<p>Rust. <a href="https://www.rust-lang.org/en-US/install.html">Install Rust</a>. Once Rust is installed, run</p>
|
||
<pre><code class="language-shell">rustup target add wasm32-unknown-unknown
|
||
</code></pre>
|
||
</li>
|
||
</ol>
|
||
<ol start="2">
|
||
<li>The tests for this project use Node. Make sure you have node >= 10 installed,
|
||
as that is when WebAssembly support was introduced. <a href="https://nodejs.org/en/">Install Node</a>.</li>
|
||
</ol>
|
||
<h2><a class="header" href="#code-formatting" id="code-formatting">Code Formatting</a></h2>
|
||
<p>Although formatting rules are not mandatory, it is encouraged to run <code>cargo run</code> (<code>rustfmt</code>) with its default rules within a PR to maintain a more organized code base. If necessary, a PR with a single commit that formats the entire project is also welcome.</p>
|
||
<h1><a class="header" href="#running-wasm-bindgens-tests" id="running-wasm-bindgens-tests">Running <code>wasm-bindgen</code>'s Tests</a></h1>
|
||
<h2><a class="header" href="#wasm-tests-on-node-and-headless-browsers" id="wasm-tests-on-node-and-headless-browsers">Wasm Tests on Node and Headless Browsers</a></h2>
|
||
<p>These are the largest test suites, and most common to run in day to day
|
||
<code>wasm-bindgen</code> development. These tests are compiled to Wasm and then run in
|
||
Node.js or a headless browser via the WebDriver protocol.</p>
|
||
<pre><code class="language-bash">cargo test --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
<p>See <a href="https://github.com/rustwasm/wasm-bindgen/blob/master/crates/test/README.md">the <code>wasm-bindgen-test</code> crate's
|
||
<code>README.md</code></a>
|
||
for details and configuring which headless browser is used.</p>
|
||
<h2><a class="header" href="#sanity-tests-for-wasm-bindgen-on-the-native-host-target" id="sanity-tests-for-wasm-bindgen-on-the-native-host-target">Sanity Tests for <code>wasm-bindgen</code> on the Native Host Target</a></h2>
|
||
<p>This small test suite just verifies that exported <code>wasm-bindgen</code> methods can
|
||
still be used on the native host's target.</p>
|
||
<pre><code>cargo test
|
||
</code></pre>
|
||
<h2><a class="header" href="#the-web-idl-frontends-tests" id="the-web-idl-frontends-tests">The Web IDL Frontend's Tests</a></h2>
|
||
<pre><code>cargo test -p webidl-tests --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
<h2><a class="header" href="#the-macro-ui-tests" id="the-macro-ui-tests">The Macro UI Tests</a></h2>
|
||
<p>These tests assert that we have reasonable error messages that point to the
|
||
right source spans when the <code>#[wasm_bindgen]</code> proc-macro is misused.</p>
|
||
<pre><code>cargo test -p ui-tests
|
||
</code></pre>
|
||
<h2><a class="header" href="#the-js-sys-tests" id="the-js-sys-tests">The <code>js-sys</code> Tests</a></h2>
|
||
<p>See <a href="contributing/js-sys/testing.html">the <code>js-sys</code> testing page</a>.</p>
|
||
<h2><a class="header" href="#the-web-sys-tests" id="the-web-sys-tests">The <code>web-sys</code> Tests</a></h2>
|
||
<p>See <a href="contributing/web-sys/testing.html">the <code>web-sys</code> testing page</a>.</p>
|
||
<h1><a class="header" href="#design-of-wasm-bindgen" id="design-of-wasm-bindgen">Design of <code>wasm-bindgen</code></a></h1>
|
||
<p>This section is intended to be a deep-dive into how <code>wasm-bindgen</code> internally
|
||
works today, specifically for Rust. If you're reading this far in the future it
|
||
may no longer be up to date, but feel free to open an issue and we can try to
|
||
answer questions and/or update this!</p>
|
||
<h2><a class="header" href="#foundation-es-modules" id="foundation-es-modules">Foundation: ES Modules</a></h2>
|
||
<p>The first thing to know about <code>wasm-bindgen</code> is that it's fundamentally built on
|
||
the idea of ES Modules. In other words this tool takes an opinionated stance
|
||
that wasm files <em>should be viewed as ES modules</em>. This means that you can
|
||
<code>import</code> from a wasm file, use its <code>export</code>-ed functionality, etc, from normal
|
||
JS files.</p>
|
||
<p>Now unfortunately at the time of this writing the interface of wasm interop
|
||
isn't very rich. Wasm modules can only call functions or export functions that
|
||
deal exclusively with <code>i32</code>, <code>i64</code>, <code>f32</code>, and <code>f64</code>. Bummer!</p>
|
||
<p>That's where this project comes in. The goal of <code>wasm-bindgen</code> is to enhance the
|
||
"ABI" of wasm modules with richer types like classes, JS objects, Rust structs,
|
||
strings, etc. Keep in mind, though, that everything is based on ES Modules! This
|
||
means that the compiler is actually producing a "broken" wasm file of sorts. The
|
||
wasm file emitted by rustc, for example, does not have the interface we would
|
||
like to have. Instead it requires the <code>wasm-bindgen</code> tool to postprocess the
|
||
file, generating a <code>foo.js</code> and <code>foo_bg.wasm</code> file. The <code>foo.js</code> file is the
|
||
desired interface expressed in JS (classes, types, strings, etc) and the
|
||
<code>foo_bg.wasm</code> module is simply used as an implementation detail (it was
|
||
lightly modified from the original <code>foo.wasm</code> file).</p>
|
||
<p>As more features are stabilized in WebAssembly over time (like host bindings)
|
||
the JS file is expected to get smaller and smaller. It's unlikely to ever
|
||
disappear, but <code>wasm-bindgen</code> is designed to follow the WebAssembly spec and
|
||
proposals closely to optimize JS/Rust as much as possible.</p>
|
||
<h2><a class="header" href="#foundation-2-unintrusive-in-rust" id="foundation-2-unintrusive-in-rust">Foundation #2: Unintrusive in Rust</a></h2>
|
||
<p>On the more Rust-y side of things the <code>wasm-bindgen</code> crate is designed to
|
||
ideally have as minimal impact on a Rust crate as possible. Ideally a few
|
||
<code>#[wasm_bindgen]</code> attributes are annotated in key locations and otherwise you're
|
||
off to the races. The attribute strives to both not invent new syntax and work
|
||
with existing idioms today.</p>
|
||
<p>For example a library might exposed a function in normal Rust that looks like:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub fn greet(name: &str) -> String {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>And with <code>#[wasm_bindgen]</code> all you need to do in exporting it to JS is:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
pub fn greet(name: &str) -> String {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Additionally the design here with minimal intervention in Rust should allow us
|
||
to easily take advantage of the upcoming <a href="https://github.com/WebAssembly/host-bindings">host bindings</a> proposal. Ideally
|
||
you'd simply upgrade <code>wasm-bindgen</code>-the-crate as well as your toolchain and
|
||
you're immediately getting raw access to host bindings! (this is still a bit of
|
||
a ways off though...)</p>
|
||
<h1><a class="header" href="#polyfill-for-js-objects-in-wasm" id="polyfill-for-js-objects-in-wasm">Polyfill for "JS objects in wasm"</a></h1>
|
||
<p>One of the main goals of <code>wasm-bindgen</code> is to allow working with and passing
|
||
around JS objects in wasm, but that's not allowed today! While indeed true,
|
||
that's where the polyfill comes in.</p>
|
||
<p>The question here is how we shoehorn JS objects into a <code>u32</code> for wasm to use.
|
||
The current strategy for this approach is to maintain a module-local variable
|
||
in the generated <code>foo.js</code> file: a <code>heap</code>.</p>
|
||
<h3><a class="header" href="#temporary-js-objects-on-the-stack" id="temporary-js-objects-on-the-stack">Temporary JS objects on the "stack"</a></h3>
|
||
<p>The first slots in the <code>heap</code> in <code>foo.js</code> are considered a stack. This stack,
|
||
like typical program execution stacks, grows down. JS objects are pushed on the
|
||
bottom of the stack, and their index in the stack is the identifier that's passed
|
||
to wasm. A stack pointer is maintained to figure out where the next item is
|
||
pushed.</p>
|
||
<p>JS objects are then only removed from the bottom of the stack as well. Removal
|
||
is simply storing null then incrementing a counter. Because of the "stack-y"
|
||
nature of this scheme it only works for when wasm doesn't hold onto a JS object
|
||
(aka it only gets a "reference" in Rust parlance).</p>
|
||
<p>Let's take a look at an example.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// foo.rs
|
||
#[wasm_bindgen]
|
||
pub fn foo(a: &JsValue) {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here we're using the special <code>JsValue</code> type from the <code>wasm-bindgen</code> library
|
||
itself. Our exported function, <code>foo</code>, takes a <em>reference</em> to an object. This
|
||
notably means that it can't persist the object past the lifetime of this
|
||
function call.</p>
|
||
<p>Now what we actually want to generate is a JS module that looks like (in
|
||
TypeScript parlance)</p>
|
||
<pre><code class="language-ts">// foo.d.ts
|
||
export function foo(a: any);
|
||
</code></pre>
|
||
<p>and what we actually generate looks something like:</p>
|
||
<pre><code class="language-js">// foo.js
|
||
import * as wasm from './foo_bg';
|
||
|
||
const heap = new Array(32);
|
||
heap.push(undefined, null, true, false);
|
||
let stack_pointer = 32;
|
||
|
||
function addBorrowedObject(obj) {
|
||
stack_pointer -= 1;
|
||
heap[stack_pointer] = obj;
|
||
return stack_pointer;
|
||
}
|
||
|
||
export function foo(arg0) {
|
||
const idx0 = addBorrowedObject(arg0);
|
||
try {
|
||
wasm.foo(idx0);
|
||
} finally {
|
||
heap[stack_pointer++] = undefined;
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>Here we can see a few notable points of action:</p>
|
||
<ul>
|
||
<li>The wasm file was renamed to <code>foo_bg.wasm</code>, and we can see how the JS module
|
||
generated here is importing from the wasm file.</li>
|
||
<li>Next we can see our <code>heap</code> module variable which is to store all JS values
|
||
reference-able from wasm.</li>
|
||
<li>Our exported function <code>foo</code>, takes an arbitrary argument, <code>arg0</code>, which is
|
||
converted to an index with the <code>addBorrowedObject</code> object function. The index
|
||
is then passed to wasm so wasm can operate with it.</li>
|
||
<li>Finally, we have a <code>finally</code> which frees the stack slot as it's no longer
|
||
used, popping the value that was pushed at the start of the function.</li>
|
||
</ul>
|
||
<p>It's also helpful to dig into the Rust side of things to see what's going on
|
||
there! Let's take a look at the code that <code>#[wasm_bindgen]</code> generates in Rust:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// what the user wrote
|
||
pub fn foo(a: &JsValue) {
|
||
// ...
|
||
}
|
||
|
||
#[export_name = "foo"]
|
||
pub extern "C" fn __wasm_bindgen_generated_foo(arg0: u32) {
|
||
let arg0 = unsafe {
|
||
ManuallyDrop::new(JsValue::__from_idx(arg0))
|
||
};
|
||
let arg0 = &*arg0;
|
||
foo(arg0);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>And as with the JS, the notable points here are:</p>
|
||
<ul>
|
||
<li>The original function, <code>foo</code>, is unmodified in the output</li>
|
||
<li>A generated function here (with a unique name) is the one that's actually
|
||
exported from the wasm module</li>
|
||
<li>Our generated function takes an integer argument (our index) and then wraps it
|
||
in a <code>JsValue</code>. There's some trickery here that's not worth going into just
|
||
yet, but we'll see in a bit what's happening under the hood.</li>
|
||
</ul>
|
||
<h3><a class="header" href="#long-lived-js-objects" id="long-lived-js-objects">Long-lived JS objects</a></h3>
|
||
<p>The above strategy is useful when JS objects are only temporarily used in Rust,
|
||
for example only during one function call. Sometimes, though, objects may have a
|
||
dynamic lifetime or otherwise need to be stored on Rust's heap. To cope with
|
||
this there's a second half of management of JS objects, naturally corresponding
|
||
to the other side of the JS <code>heap</code> array.</p>
|
||
<p>JS Objects passed to wasm that are not references are assumed to have a dynamic
|
||
lifetime inside of the wasm module. As a result the strict push/pop of the stack
|
||
won't work and we need more permanent storage for the JS objects. To cope with
|
||
this we build our own "slab allocator" of sorts.</p>
|
||
<p>A picture (or code) is worth a thousand words so let's show what happens with an
|
||
example.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// foo.rs
|
||
#[wasm_bindgen]
|
||
pub fn foo(a: JsValue) {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Note that the <code>&</code> is missing in front of the <code>JsValue</code> we had before, and in
|
||
Rust parlance this means it's taking ownership of the JS value. The exported ES
|
||
module interface is the same as before, but the ownership mechanics are slightly
|
||
different. Let's see the generated JS's slab in action:</p>
|
||
<pre><code class="language-js">import * as wasm from './foo_bg'; // imports from wasm file
|
||
|
||
const heap = new Array(32);
|
||
heap.push(undefined, null, true, false);
|
||
let heap_next = 36;
|
||
|
||
function addHeapObject(obj) {
|
||
if (heap_next === heap.length)
|
||
heap.push(heap.length + 1);
|
||
const idx = heap_next;
|
||
heap_next = heap[idx];
|
||
heap[idx] = obj;
|
||
return idx;
|
||
}
|
||
|
||
export function foo(arg0) {
|
||
const idx0 = addHeapObject(arg0);
|
||
wasm.foo(idx0);
|
||
}
|
||
|
||
export function __wbindgen_object_drop_ref(idx) {
|
||
heap[idx ] = heap_next;
|
||
heap_next = idx;
|
||
}
|
||
</code></pre>
|
||
<p>Unlike before we're now calling <code>addHeapObject</code> on the argument to <code>foo</code> rather
|
||
than <code>addBorrowedObject</code>. This function will use <code>heap</code> and <code>heap_next</code> as a
|
||
slab allocator to acquire a slot to store the object, placing a structure there
|
||
once it's found. Note that this is going on the right-half of the array, unlike
|
||
the stack which resides on the left half. This discipline mirrors the stack/heap
|
||
in normal programs, roughly.</p>
|
||
<p>Another curious aspect of this generated module is the
|
||
<code>__wbindgen_object_drop_ref</code> function. This is one that's actually imported to
|
||
wasm rather than used in this module! This function is used to signal the end of
|
||
the lifetime of a <code>JsValue</code> in Rust, or in other words when it goes out of
|
||
scope. Otherwise though this function is largely just a general "slab free"
|
||
implementation.</p>
|
||
<p>And finally, let's take a look at the Rust generated again too:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// what the user wrote
|
||
pub fn foo(a: JsValue) {
|
||
// ...
|
||
}
|
||
|
||
#[export_name = "foo"]
|
||
pub extern "C" fn __wasm_bindgen_generated_foo(arg0: u32) {
|
||
let arg0 = unsafe {
|
||
JsValue::__from_idx(arg0)
|
||
};
|
||
foo(arg0);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Ah that looks much more familiar! Not much interesting is happening here, so
|
||
let's move on to...</p>
|
||
<h3><a class="header" href="#anatomy-of-jsvalue" id="anatomy-of-jsvalue">Anatomy of <code>JsValue</code></a></h3>
|
||
<p>Currently the <code>JsValue</code> struct is actually quite simple in Rust, it's:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub struct JsValue {
|
||
idx: u32,
|
||
}
|
||
|
||
// "private" constructors
|
||
|
||
impl Drop for JsValue {
|
||
fn drop(&mut self) {
|
||
unsafe {
|
||
__wbindgen_object_drop_ref(self.idx);
|
||
}
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Or in other words it's a newtype wrapper around a <code>u32</code>, the index that we're
|
||
passed from wasm. The destructor here is where the <code>__wbindgen_object_drop_ref</code>
|
||
function is called to relinquish our reference count of the JS object, freeing
|
||
up our slot in the <code>slab</code> that we saw above.</p>
|
||
<p>If you'll recall as well, when we took <code>&JsValue</code> above we generated a wrapper
|
||
of <code>ManuallyDrop</code> around the local binding, and that's because we wanted to
|
||
avoid invoking this destructor when the object comes from the stack.</p>
|
||
<h3><a class="header" href="#working-with-heap-in-reality" id="working-with-heap-in-reality">Working with <code>heap</code> in reality</a></h3>
|
||
<p>The above explanations are pretty close to what happens today, but in reality
|
||
there's a few differences especially around handling constant values like
|
||
<code>undefined</code>, <code>null</code>, etc. Be sure to check out the actual generated JS and the
|
||
generation code for the full details!</p>
|
||
<h1><a class="header" href="#exporting-a-function-to-js" id="exporting-a-function-to-js">Exporting a function to JS</a></h1>
|
||
<p>Alright now that we've got a good grasp on JS objects and how they're working,
|
||
let's take a look at another feature of <code>wasm-bindgen</code>: exporting functionality
|
||
with types that are richer than just numbers.</p>
|
||
<p>The basic idea around exporting functionality with more flavorful types is that
|
||
the wasm exports won't actually be called directly. Instead the generated
|
||
<code>foo.js</code> module will have shims for all exported functions in the wasm module.</p>
|
||
<p>The most interesting conversion here happens with strings so let's take a look
|
||
at that.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
pub fn greet(a: &str) -> String {
|
||
format!("Hello, {}!", a)
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here we'd like to define an ES module that looks like</p>
|
||
<pre><code class="language-ts">// foo.d.ts
|
||
export function greet(a: string): string;
|
||
</code></pre>
|
||
<p>To see what's going on, let's take a look at the generated shim</p>
|
||
<pre><code class="language-js">import * as wasm from './foo_bg';
|
||
|
||
function passStringToWasm(arg) {
|
||
const buf = new TextEncoder('utf-8').encode(arg);
|
||
const len = buf.length;
|
||
const ptr = wasm.__wbindgen_malloc(len);
|
||
let array = new Uint8Array(wasm.memory.buffer);
|
||
array.set(buf, ptr);
|
||
return [ptr, len];
|
||
}
|
||
|
||
function getStringFromWasm(ptr, len) {
|
||
const mem = new Uint8Array(wasm.memory.buffer);
|
||
const slice = mem.slice(ptr, ptr + len);
|
||
const ret = new TextDecoder('utf-8').decode(slice);
|
||
return ret;
|
||
}
|
||
|
||
export function greet(arg0) {
|
||
const [ptr0, len0] = passStringToWasm(arg0);
|
||
try {
|
||
const ret = wasm.greet(ptr0, len0);
|
||
const ptr = wasm.__wbindgen_boxed_str_ptr(ret);
|
||
const len = wasm.__wbindgen_boxed_str_len(ret);
|
||
const realRet = getStringFromWasm(ptr, len);
|
||
wasm.__wbindgen_boxed_str_free(ret);
|
||
return realRet;
|
||
} finally {
|
||
wasm.__wbindgen_free(ptr0, len0);
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>Phew, that's quite a lot! We can sort of see though if we look closely what's
|
||
happening:</p>
|
||
<ul>
|
||
<li>
|
||
<p>Strings are passed to wasm via two arguments, a pointer and a length. Right
|
||
now we have to copy the string onto the wasm heap which means we'll be using
|
||
<code>TextEncoder</code> to actually do the encoding. Once this is done we use an
|
||
internal function in <code>wasm-bindgen</code> to allocate space for the string to go,
|
||
and then we'll pass that ptr/length to wasm later on.</p>
|
||
</li>
|
||
<li>
|
||
<p>Returning strings from wasm is a little tricky as we need to return a ptr/len
|
||
pair, but wasm currently only supports one return value (multiple return values
|
||
<a href="https://github.com/WebAssembly/design/issues/1146">is being standardized</a>).
|
||
To work around this in the meantime, we're actually returning a pointer to a
|
||
ptr/len pair, and then using functions to access the various fields.</p>
|
||
</li>
|
||
<li>
|
||
<p>Some cleanup ends up happening in wasm. The <code>__wbindgen_boxed_str_free</code>
|
||
function is used to free the return value of <code>greet</code> after it's been decoded
|
||
onto the JS heap (using <code>TextDecoder</code>). The <code>__wbindgen_free</code> is then used to
|
||
free the space we allocated to pass the string argument once the function call
|
||
is done.</p>
|
||
</li>
|
||
</ul>
|
||
<p>Next let's take a look at the Rust side of things as well. Here we'll be looking
|
||
at a mostly abbreviated and/or "simplified" in the sense of this is what it
|
||
compiles down to:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub extern "C" fn greet(a: &str) -> String {
|
||
format!("Hello, {}!", a)
|
||
}
|
||
|
||
#[export_name = "greet"]
|
||
pub extern "C" fn __wasm_bindgen_generated_greet(
|
||
arg0_ptr: *const u8,
|
||
arg0_len: usize,
|
||
) -> *mut String {
|
||
let arg0 = unsafe {
|
||
let slice = ::std::slice::from_raw_parts(arg0_ptr, arg0_len);
|
||
::std::str::from_utf8_unchecked(slice)
|
||
};
|
||
let _ret = greet(arg0);
|
||
Box::into_raw(Box::new(_ret))
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here we can see again that our <code>greet</code> function is unmodified and has a wrapper
|
||
to call it. This wrapper will take the ptr/len argument and convert it to a
|
||
string slice, while the return value is boxed up into just a pointer and is
|
||
then returned up to was for reading via the <code>__wbindgen_boxed_str_*</code> functions.</p>
|
||
<p>So in general exporting a function involves a shim both in JS and in Rust with
|
||
each side translating to or from wasm arguments to the native types of each
|
||
language. The <code>wasm-bindgen</code> tool manages hooking up all these shims while the
|
||
<code>#[wasm_bindgen]</code> macro takes care of the Rust shim as well.</p>
|
||
<p>Most arguments have a relatively clear way to convert them, bit if you've got
|
||
any questions just let me know!</p>
|
||
<h1><a class="header" href="#exporting-a-struct-to-js" id="exporting-a-struct-to-js">Exporting a struct to JS</a></h1>
|
||
<p>So far we've covered JS objects, importing functions, and exporting functions.
|
||
This has given us quite a rich base to build on so far, and that's great! We
|
||
sometimes, though, want to go even further and define a JS <code>class</code> in Rust. Or
|
||
in other words, we want to expose an object with methods from Rust to JS rather
|
||
than just importing/exporting free functions.</p>
|
||
<p>The <code>#[wasm_bindgen]</code> attribute can annotate both a <code>struct</code> and <code>impl</code> blocks
|
||
to allow:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
pub struct Foo {
|
||
internal: i32,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl Foo {
|
||
pub fn new(val: i32) -> Foo {
|
||
Foo { internal: val }
|
||
}
|
||
|
||
pub fn get(&self) -> i32 {
|
||
self.internal
|
||
}
|
||
|
||
pub fn set(&mut self, val: i32) {
|
||
self.internal = val;
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>This is a typical Rust <code>struct</code> definition for a type with a constructor and a
|
||
few methods. Annotating the struct with <code>#[wasm_bindgen]</code> means that we'll
|
||
generate necessary trait impls to convert this type to/from the JS boundary. The
|
||
annotated <code>impl</code> block here means that the functions inside will also be made
|
||
available to JS through generated shims. If we take a look at the generated JS
|
||
code for this we'll see:</p>
|
||
<pre><code class="language-js">import * as wasm from './js_hello_world_bg';
|
||
|
||
export class Foo {
|
||
static __construct(ptr) {
|
||
return new Foo(ptr);
|
||
}
|
||
|
||
constructor(ptr) {
|
||
this.ptr = ptr;
|
||
}
|
||
|
||
free() {
|
||
const ptr = this.ptr;
|
||
this.ptr = 0;
|
||
wasm.__wbg_foo_free(ptr);
|
||
}
|
||
|
||
static new(arg0) {
|
||
const ret = wasm.foo_new(arg0);
|
||
return Foo.__construct(ret)
|
||
}
|
||
|
||
get() {
|
||
const ret = wasm.foo_get(this.ptr);
|
||
return ret;
|
||
}
|
||
|
||
set(arg0) {
|
||
const ret = wasm.foo_set(this.ptr, arg0);
|
||
return ret;
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>That's actually not much! We can see here though how we've translated from Rust
|
||
to JS:</p>
|
||
<ul>
|
||
<li>Associated functions in Rust (those without <code>self</code>) turn into <code>static</code>
|
||
functions in JS.</li>
|
||
<li>Methods in Rust turn into methods in wasm.</li>
|
||
<li>Manual memory management is exposed in JS as well. The <code>free</code> function is
|
||
required to be invoked to deallocate resources on the Rust side of things.</li>
|
||
</ul>
|
||
<p>To be able to use <code>new Foo()</code>, you'd need to annotate <code>new</code> as <code>#[wasm_bindgen(constructor)]</code>.</p>
|
||
<p>One important aspect to note here, though, is that once <code>free</code> is called the JS
|
||
object is "neutered" in that its internal pointer is nulled out. This means that
|
||
future usage of this object should trigger a panic in Rust.</p>
|
||
<p>The real trickery with these bindings ends up happening in Rust, however, so
|
||
let's take a look at that.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
// original input to `#[wasm_bindgen]` omitted ...
|
||
|
||
#[export_name = "foo_new"]
|
||
pub extern "C" fn __wasm_bindgen_generated_Foo_new(arg0: i32) -> u32
|
||
let ret = Foo::new(arg0);
|
||
Box::into_raw(Box::new(WasmRefCell::new(ret))) as u32
|
||
}
|
||
|
||
#[export_name = "foo_get"]
|
||
pub extern "C" fn __wasm_bindgen_generated_Foo_get(me: u32) -> i32 {
|
||
let me = me as *mut WasmRefCell<Foo>;
|
||
wasm_bindgen::__rt::assert_not_null(me);
|
||
let me = unsafe { &*me };
|
||
return me.borrow().get();
|
||
}
|
||
|
||
#[export_name = "foo_set"]
|
||
pub extern "C" fn __wasm_bindgen_generated_Foo_set(me: u32, arg1: i32) {
|
||
let me = me as *mut WasmRefCell<Foo>;
|
||
wasm_bindgen::__rt::assert_not_null(me);
|
||
let me = unsafe { &*me };
|
||
me.borrow_mut().set(arg1);
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub unsafe extern "C" fn __wbindgen_foo_free(me: u32) {
|
||
let me = me as *mut WasmRefCell<Foo>;
|
||
wasm_bindgen::__rt::assert_not_null(me);
|
||
(*me).borrow_mut(); // ensure no active borrows
|
||
drop(Box::from_raw(me));
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>As with before this is cleaned up from the actual output but it's the same idea
|
||
as to what's going on! Here we can see a shim for each function as well as a
|
||
shim for deallocating an instance of <code>Foo</code>. Recall that the only valid wasm
|
||
types today are numbers, so we're required to shoehorn all of <code>Foo</code> into a
|
||
<code>u32</code>, which is currently done via <code>Box</code> (like <code>std::unique_ptr</code> in C++).
|
||
Note, though, that there's an extra layer here, <code>WasmRefCell</code>. This type is the
|
||
same as <a href="https://doc.rust-lang.org/std/cell/struct.RefCell.html"><code>RefCell</code></a> and can be mostly glossed over.</p>
|
||
<p>The purpose for this type, if you're interested though, is to uphold Rust's
|
||
guarantees about aliasing in a world where aliasing is rampant (JS).
|
||
Specifically the <code>&Foo</code> type means that there can be as much aliasing as you'd
|
||
like, but crucially <code>&mut Foo</code> means that it is the sole pointer to the data
|
||
(no other <code>&Foo</code> to the same instance exists). The <a href="https://doc.rust-lang.org/std/cell/struct.RefCell.html"><code>RefCell</code></a> type in libstd
|
||
is a way of dynamically enforcing this at runtime (as opposed to compile time
|
||
where it usually happens). Baking in <code>WasmRefCell</code> is the same idea here,
|
||
adding runtime checks for aliasing which are typically happening at compile
|
||
time. This is currently a Rust-specific feature which isn't actually in the
|
||
<code>wasm-bindgen</code> tool itself, it's just in the Rust-generated code (aka the
|
||
<code>#[wasm_bindgen]</code> attribute).</p>
|
||
<h1><a class="header" href="#importing-a-function-from-js" id="importing-a-function-from-js">Importing a function from JS</a></h1>
|
||
<p>Now that we've exported some rich functionality to JS it's also time to import
|
||
some! The goal here is to basically implement JS <code>import</code> statements in Rust,
|
||
with fancy types and all.</p>
|
||
<p>First up, let's say we invert the function above and instead want to generate
|
||
greetings in JS but call it from Rust. We might have, for example:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(module = "./greet")]
|
||
extern "C" {
|
||
fn greet(a: &str) -> String;
|
||
}
|
||
|
||
fn other_code() {
|
||
let greeting = greet("foo");
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>The basic idea of imports is the same as exports in that we'll have shims in
|
||
both JS and Rust doing the necessary translation. Let's first see the JS shim in
|
||
action:</p>
|
||
<pre><code class="language-js">import * as wasm from './foo_bg';
|
||
|
||
import { greet } from './greet';
|
||
|
||
// ...
|
||
|
||
export function __wbg_f_greet(ptr0, len0, wasmretptr) {
|
||
const [retptr, retlen] = passStringToWasm(greet(getStringFromWasm(ptr0, len0)));
|
||
(new Uint32Array(wasm.memory.buffer))[wasmretptr / 4] = retlen;
|
||
return retptr;
|
||
}
|
||
</code></pre>
|
||
<p>The <code>getStringFromWasm</code> and <code>passStringToWasm</code> are the same as we saw before,
|
||
and like with <code>__wbindgen_object_drop_ref</code> far above we've got this weird export
|
||
from our module now! The <code>__wbg_f_greet</code> function is what's generated by
|
||
<code>wasm-bindgen</code> to actually get imported in the <code>foo.wasm</code> module.</p>
|
||
<p>The generated <code>foo.js</code> we see imports from the <code>./greet</code> module with the <code>greet</code>
|
||
name (was the function import in Rust said) and then the <code>__wbg_f_greet</code>
|
||
function is shimming that import.</p>
|
||
<p>There's some tricky ABI business going on here so let's take a look at the
|
||
generated Rust as well. Like before this is simplified from what's actually
|
||
generated.</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
extern "C" fn greet(a: &str) -> String {
|
||
extern "C" {
|
||
fn __wbg_f_greet(a_ptr: *const u8, a_len: usize, ret_len: *mut usize) -> *mut u8;
|
||
}
|
||
unsafe {
|
||
let a_ptr = a.as_ptr();
|
||
let a_len = a.len();
|
||
let mut __ret_strlen = 0;
|
||
let mut __ret_strlen_ptr = &mut __ret_strlen as *mut usize;
|
||
let _ret = __wbg_f_greet(a_ptr, a_len, __ret_strlen_ptr);
|
||
String::from_utf8_unchecked(
|
||
Vec::from_raw_parts(_ret, __ret_strlen, __ret_strlen)
|
||
)
|
||
}
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Here we can see that the <code>greet</code> function was generated but it's largely just a
|
||
shim around the <code>__wbg_f_greet</code> function that we're calling. The ptr/len pair
|
||
for the argument is passed as two arguments and for the return value we're
|
||
receiving one value (the length) indirectly while directly receiving the
|
||
returned pointer.</p>
|
||
<h1><a class="header" href="#importing-a-class-from-js" id="importing-a-class-from-js">Importing a class from JS</a></h1>
|
||
<p>Just like with functions after we've started exporting we'll also want to
|
||
import! Now that we've exported a <code>class</code> to JS we'll want to also be able to
|
||
import classes in Rust as well to invoke methods and such. Since JS classes are
|
||
in general just JS objects the bindings here will look pretty similar to the JS
|
||
object bindings describe above.</p>
|
||
<p>As usual though, let's dive into an example!</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen(module = "./bar")]
|
||
extern "C" {
|
||
type Bar;
|
||
|
||
#[wasm_bindgen(constructor)]
|
||
fn new(arg: i32) -> Bar;
|
||
|
||
#[wasm_bindgen(js_namespace = Bar)]
|
||
fn another_function() -> i32;
|
||
|
||
#[wasm_bindgen(method)]
|
||
fn get(this: &Bar) -> i32;
|
||
|
||
#[wasm_bindgen(method)]
|
||
fn set(this: &Bar, val: i32);
|
||
|
||
#[wasm_bindgen(method, getter)]
|
||
fn property(this: &Bar) -> i32;
|
||
|
||
#[wasm_bindgen(method, setter)]
|
||
fn set_property(this: &Bar, val: i32);
|
||
}
|
||
|
||
fn run() {
|
||
let bar = Bar::new(Bar::another_function());
|
||
let x = bar.get();
|
||
bar.set(x + 3);
|
||
|
||
bar.set_property(bar.property() + 6);
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>Unlike our previous imports, this one's a bit more chatty! Remember that one of
|
||
the goals of <code>wasm-bindgen</code> is to use native Rust syntax wherever possible, so
|
||
this is mostly intended to use the <code>#[wasm_bindgen]</code> attribute to interpret
|
||
what's written down in Rust. Now there's a few attribute annotations here, so
|
||
let's go through one-by-one:</p>
|
||
<ul>
|
||
<li><code>#[wasm_bindgen(module = "./bar")]</code> - seen before with imports this is declare
|
||
where all the subsequent functionality is imported from. For example the <code>Bar</code>
|
||
type is going to be imported from the <code>./bar</code> module.</li>
|
||
<li><code>type Bar</code> - this is a declaration of JS class as a new type in Rust. This
|
||
means that a new type <code>Bar</code> is generated which is "opaque" but is represented
|
||
as internally containing a <code>JsValue</code>. We'll see more on this later.</li>
|
||
<li><code>#[wasm_bindgen(constructor)]</code> - this indicates that the binding's name isn't
|
||
actually used in JS but rather translates to <code>new Bar()</code>. The return value of
|
||
this function must be a bare type, like <code>Bar</code>.</li>
|
||
<li><code>#[wasm_bindgen(js_namespace = Bar)]</code> - this attribute indicates that the
|
||
function declaration is namespaced through the <code>Bar</code> class in JS.</li>
|
||
<li><code>#[wasm_bindgen(static_method_of = SomeJsClass)]</code> - this attribute is similar
|
||
to <code>js_namespace</code>, but instead of producing a free function, produces a static
|
||
method of <code>SomeJsClass</code>.</li>
|
||
<li><code>#[wasm_bindgen(method)]</code> - and finally, this attribute indicates that a
|
||
method call is going to happen. The first argument must be a JS struct, like
|
||
<code>Bar</code>, and the call in JS looks like <code>Bar.prototype.set.call(...)</code>.</li>
|
||
</ul>
|
||
<p>With all that in mind, let's take a look at the JS generated.</p>
|
||
<pre><code class="language-js">import * as wasm from './foo_bg';
|
||
|
||
import { Bar } from './bar';
|
||
|
||
// other support functions omitted...
|
||
|
||
export function __wbg_s_Bar_new() {
|
||
return addHeapObject(new Bar());
|
||
}
|
||
|
||
const another_function_shim = Bar.another_function;
|
||
export function __wbg_s_Bar_another_function() {
|
||
return another_function_shim();
|
||
}
|
||
|
||
const get_shim = Bar.prototype.get;
|
||
export function __wbg_s_Bar_get(ptr) {
|
||
return shim.call(getObject(ptr));
|
||
}
|
||
|
||
const set_shim = Bar.prototype.set;
|
||
export function __wbg_s_Bar_set(ptr, arg0) {
|
||
set_shim.call(getObject(ptr), arg0)
|
||
}
|
||
|
||
const property_shim = Object.getOwnPropertyDescriptor(Bar.prototype, 'property').get;
|
||
export function __wbg_s_Bar_property(ptr) {
|
||
return property_shim.call(getObject(ptr));
|
||
}
|
||
|
||
const set_property_shim = Object.getOwnPropertyDescriptor(Bar.prototype, 'property').set;
|
||
export function __wbg_s_Bar_set_property(ptr, arg0) {
|
||
set_property_shim.call(getObject(ptr), arg0)
|
||
}
|
||
</code></pre>
|
||
<p>Like when importing functions from JS we can see a bunch of shims are generated
|
||
for all the relevant functions. The <code>new</code> static function has the
|
||
<code>#[wasm_bindgen(constructor)]</code> attribute which means that instead of any
|
||
particular method it should actually invoke the <code>new</code> constructor instead (as
|
||
we see here). The static function <code>another_function</code>, however, is dispatched as
|
||
<code>Bar.another_function</code>.</p>
|
||
<p>The <code>get</code> and <code>set</code> functions are methods so they go through <code>Bar.prototype</code>,
|
||
and otherwise their first argument is implicitly the JS object itself which is
|
||
loaded through <code>getObject</code> like we saw earlier.</p>
|
||
<p>Some real meat starts to show up though on the Rust side of things, so let's
|
||
take a look:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub struct Bar {
|
||
obj: JsValue,
|
||
}
|
||
|
||
impl Bar {
|
||
fn new() -> Bar {
|
||
extern "C" {
|
||
fn __wbg_s_Bar_new() -> u32;
|
||
}
|
||
unsafe {
|
||
let ret = __wbg_s_Bar_new();
|
||
Bar { obj: JsValue::__from_idx(ret) }
|
||
}
|
||
}
|
||
|
||
fn another_function() -> i32 {
|
||
extern "C" {
|
||
fn __wbg_s_Bar_another_function() -> i32;
|
||
}
|
||
unsafe {
|
||
__wbg_s_Bar_another_function()
|
||
}
|
||
}
|
||
|
||
fn get(&self) -> i32 {
|
||
extern "C" {
|
||
fn __wbg_s_Bar_get(ptr: u32) -> i32;
|
||
}
|
||
unsafe {
|
||
let ptr = self.obj.__get_idx();
|
||
let ret = __wbg_s_Bar_get(ptr);
|
||
return ret
|
||
}
|
||
}
|
||
|
||
fn set(&self, val: i32) {
|
||
extern "C" {
|
||
fn __wbg_s_Bar_set(ptr: u32, val: i32);
|
||
}
|
||
unsafe {
|
||
let ptr = self.obj.__get_idx();
|
||
__wbg_s_Bar_set(ptr, val);
|
||
}
|
||
}
|
||
|
||
fn property(&self) -> i32 {
|
||
extern "C" {
|
||
fn __wbg_s_Bar_property(ptr: u32) -> i32;
|
||
}
|
||
unsafe {
|
||
let ptr = self.obj.__get_idx();
|
||
let ret = __wbg_s_Bar_property(ptr);
|
||
return ret
|
||
}
|
||
}
|
||
|
||
fn set_property(&self, val: i32) {
|
||
extern "C" {
|
||
fn __wbg_s_Bar_set_property(ptr: u32, val: i32);
|
||
}
|
||
unsafe {
|
||
let ptr = self.obj.__get_idx();
|
||
__wbg_s_Bar_set_property(ptr, val);
|
||
}
|
||
}
|
||
}
|
||
|
||
impl WasmBoundary for Bar {
|
||
// ...
|
||
}
|
||
|
||
impl ToRefWasmBoundary for Bar {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>In Rust we're seeing that a new type, <code>Bar</code>, is generated for this import of a
|
||
class. The type <code>Bar</code> internally contains a <code>JsValue</code> as an instance of <code>Bar</code>
|
||
is meant to represent a JS object stored in our module's stack/slab. This then
|
||
works mostly the same way that we saw JS objects work in the beginning.</p>
|
||
<p>When calling <code>Bar::new</code> we'll get an index back which is wrapped up in <code>Bar</code>
|
||
(which is itself just a <code>u32</code> in memory when stripped down). Each function then
|
||
passes the index as the first argument and otherwise forwards everything along
|
||
in Rust.</p>
|
||
<h1><a class="header" href="#rust-type-conversions" id="rust-type-conversions">Rust Type conversions</a></h1>
|
||
<p>Previously we've been seeing mostly abridged versions of type conversions when
|
||
values enter Rust. Here we'll go into some more depth about how this is
|
||
implemented. There are two categories of traits for converting values, traits
|
||
for converting values from Rust to JS and traits for the other way around.</p>
|
||
<h2><a class="header" href="#from-rust-to-js" id="from-rust-to-js">From Rust to JS</a></h2>
|
||
<p>First up let's take a look at going from Rust to JS:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub trait IntoWasmAbi: WasmDescribe {
|
||
type Abi: WasmAbi;
|
||
fn into_abi(self, extra: &mut Stack) -> Self::Abi;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>And that's it! This is actually the only trait needed currently for translating
|
||
a Rust value to a JS one. There's a few points here:</p>
|
||
<ul>
|
||
<li>We'll get to <code>WasmDescribe</code> later in this section</li>
|
||
<li>The associated type <code>Abi</code> is what will actually be generated as an argument to
|
||
the wasm export. The bound <code>WasmAbi</code> is only implemented for types like <code>u32</code>
|
||
and <code>f64</code>, those which can be placed on the boundary and transmitted
|
||
losslessly.</li>
|
||
<li>And finally we have the <code>into_abi</code> function, returning the <code>Abi</code> associated
|
||
type which will be actually passed to JS. There's also this <code>Stack</code> parameter,
|
||
however. Not all Rust values can be communicated in 32 bits to the <code>Stack</code>
|
||
parameter allows transmitting more data, explained in a moment.</li>
|
||
</ul>
|
||
<p>This trait is implemented for all types that can be converted to JS and is
|
||
unconditionally used during codegen. For example you'll often see <code>IntoWasmAbi for Foo</code> but also <code>IntoWasmAbi for &'a Foo</code>.</p>
|
||
<p>The <code>IntoWasmAbi</code> trait is used in two locations. First it's used to convert
|
||
return values of Rust exported functions to JS. Second it's used to convert the
|
||
Rust arguments of JS functions imported to Rust.</p>
|
||
<h2><a class="header" href="#from-js-to-rust" id="from-js-to-rust">From JS to Rust</a></h2>
|
||
<p>Unfortunately the opposite direction from above, going from JS to Rust, is a bit
|
||
more complicated. Here we've got three traits:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub trait FromWasmAbi: WasmDescribe {
|
||
type Abi: WasmAbi;
|
||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self;
|
||
}
|
||
|
||
pub trait RefFromWasmAbi: WasmDescribe {
|
||
type Abi: WasmAbi;
|
||
type Anchor: Deref<Target=Self>;
|
||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
||
}
|
||
|
||
pub trait RefMutFromWasmAbi: WasmDescribe {
|
||
type Abi: WasmAbi;
|
||
type Anchor: DerefMut<Target=Self>;
|
||
unsafe fn ref_mut_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>The <code>FromWasmAbi</code> is relatively straightforward, basically the opposite of
|
||
<code>IntoWasmAbi</code>. It takes the ABI argument (typically the same as
|
||
<code>IntoWasmAbi::Abi</code>) and then the auxiliary stack to produce an instance of
|
||
<code>Self</code>. This trait is implemented primarily for types that <em>don't</em> have internal
|
||
lifetimes or are references.</p>
|
||
<p>The latter two traits here are mostly the same, and are intended for generating
|
||
references (both shared and mutable references). They look almost the same as
|
||
<code>FromWasmAbi</code> except that they return an <code>Anchor</code> type which implements a
|
||
<code>Deref</code> trait rather than <code>Self</code>.</p>
|
||
<p>The <code>Ref*</code> traits allow having arguments in functions that are references rather
|
||
than bare types, for example <code>&str</code>, <code>&JsValue</code>, or <code>&[u8]</code>. The <code>Anchor</code> here
|
||
is required to ensure that the lifetimes don't persist beyond one function call
|
||
and remain anonymous.</p>
|
||
<p>The <code>From*</code> family of traits are used for converting the Rust arguments in Rust
|
||
exported functions to JS. They are also used for the return value in JS
|
||
functions imported into Rust.</p>
|
||
<h2><a class="header" href="#global-stack" id="global-stack">Global stack</a></h2>
|
||
<p>Mentioned above not all Rust types will fit within 32 bits. While we can
|
||
communicate an <code>f64</code> we don't necessarily have the ability to use all the bits.
|
||
Types like <code>&str</code> need to communicate two items, a pointer and a length (64
|
||
bits). Other types like <code>&Closure<Fn()></code> have even more information to
|
||
transmit.</p>
|
||
<p>As a result we need a method of communicating more data through the signatures
|
||
of functions. While we could add more arguments this is somewhat difficult to do
|
||
in the world of closures where code generation isn't quite as dynamic as a
|
||
procedural macro. Consequently a "global stack" is used to transmit extra
|
||
data for a function call.</p>
|
||
<p>The global stack is a fixed-sized static allocation in the wasm module. This
|
||
stack is temporary scratch space for any one function call from either JS to
|
||
Rust or Rust to JS. Both Rust and the JS shim generated have pointers to this
|
||
global stack and will read/write information from it.</p>
|
||
<p>Using this scheme whenever we want to pass <code>&str</code> from JS to Rust we can pass
|
||
the pointer as the actual ABI argument and the length is then placed in the next
|
||
spot on the global stack.</p>
|
||
<p>The <code>Stack</code> argument to the conversion traits above looks like:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub trait Stack {
|
||
fn push(&mut self, bits: u32);
|
||
fn pop(&mut self) -> u32;
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>A trait is used here to facilitate testing but typically the calls don't end up
|
||
being virtually dispatched at runtime.</p>
|
||
<h1><a class="header" href="#communicating-types-to-wasm-bindgen" id="communicating-types-to-wasm-bindgen">Communicating types to <code>wasm-bindgen</code></a></h1>
|
||
<p>The last aspect to talk about when converting Rust/JS types amongst one another
|
||
is how this information is actually communicated. The <code>#[wasm_bindgen]</code> macro is
|
||
running over the syntactical (unresolved) structure of the Rust code and is then
|
||
responsible for generating information that <code>wasm-bindgen</code> the CLI tool later
|
||
reads.</p>
|
||
<p>To accomplish this a slightly unconventional approach is taken. Static
|
||
information about the structure of the Rust code is serialized via JSON
|
||
(currently) to a custom section of the wasm executable. Other information, like
|
||
what the types actually are, unfortunately isn't known until later in the
|
||
compiler due to things like associated type projections and typedefs. It also
|
||
turns out that we want to convey "rich" types like <code>FnMut(String, Foo, &JsValue)</code> to the <code>wasm-bindgen</code> CLI, and handling all this is pretty tricky!</p>
|
||
<p>To solve this issue the <code>#[wasm_bindgen]</code> macro generates <strong>executable
|
||
functions</strong> which "describe the type signature of an import or export". These
|
||
executable functions are what the <code>WasmDescribe</code> trait is all about:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
pub trait WasmDescribe {
|
||
fn describe();
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>While deceptively simple this trait is actually quite important. When you write,
|
||
an export like this:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
#[wasm_bindgen]
|
||
fn greet(a: &str) {
|
||
// ...
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>In addition to the shims we talked about above which JS generates the macro
|
||
<em>also</em> generates something like:</p>
|
||
<pre><code>#[no_mangle]
|
||
pub extern "C" fn __wbindgen_describe_greet() {
|
||
<Fn(&str)>::describe();
|
||
}
|
||
</code></pre>
|
||
<p>Or in other words it generates invocations of <code>describe</code> functions. In doing so
|
||
the <code>__wbindgen_describe_greet</code> shim is a programmatic description of the type
|
||
layouts of an import/export. These are then executed when <code>wasm-bindgen</code> runs!
|
||
These executions rely on an import called <code>__wbindgen_describe</code> which passes one
|
||
<code>u32</code> to the host, and when called multiple times gives a <code>Vec<u32></code>
|
||
effectively. This <code>Vec<u32></code> can then be reparsed into an <code>enum Descriptor</code>
|
||
which fully describes a type.</p>
|
||
<p>All in all this is a bit roundabout but shouldn't have any impact on the
|
||
generated code or runtime at all. All these descriptor functions are pruned from
|
||
the emitted wasm file.</p>
|
||
<h1><a class="header" href="#js-sys" id="js-sys"><code>js-sys</code></a></h1>
|
||
<p>The <a href="https://crates.io/crates/js-sys"><code>js-sys</code> crate</a> provides raw bindings to all the global APIs
|
||
guaranteed to exist in every JavaScript environment by the ECMAScript standard,
|
||
and its source lives at <a href="https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys"><code>wasm-bindgen/crates/js-sys</code></a>. With the <code>js-sys</code>
|
||
crate, we can work with <code>Object</code>s, <code>Array</code>s, <code>Function</code>s, <code>Map</code>s, <code>Set</code>s,
|
||
etc... without writing the <code>#[wasm_bindgen]</code> imports by hand.</p>
|
||
<p>Documentation for the published version of this crate is available on
|
||
<a href="https://docs.rs/js-sys">docs.rs</a> but you can also check out the <a href="https://rustwasm.github.io/wasm-bindgen/api/js_sys/">master branch
|
||
documentation</a> for the crate.</p>
|
||
<p>For example, we can invoke JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function"><code>Function</code></a> callbacks and
|
||
time how long they take to execute with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now"><code>Date.now()</code></a>, and we
|
||
don't need to write any JS imports ourselves:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
extern crate js_sys;
|
||
extern crate wasm_bindgen;
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
#[wasm_bindgen]
|
||
pub fn timed(callback: &js_sys::Function) -> f64 {
|
||
let then = js_sys::Date::now();
|
||
callback.apply(JsValue::null(), &js_sys::Array::new()).unwrap();
|
||
let now = js_sys::Date::now();
|
||
now - then
|
||
}
|
||
#}</code></pre></pre>
|
||
<p>The <code>js-sys</code> crate doesn't contain bindings to any Web APIs like
|
||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll"><code>document.querySelectorAll</code></a>. These will be part of the
|
||
<a href="https://crates.io/crates/web-sys"><code>web-sys</code></a> crate.</p>
|
||
<h1><a class="header" href="#testing" id="testing">Testing</a></h1>
|
||
<p>You can test the <code>js-sys</code> crate by running <code>cargo test --target wasm32-unknown-unknown</code> within the <code>crates/js-sys</code> directory in the
|
||
<code>wasm-bindgen</code> repository:</p>
|
||
<pre><code class="language-sh">cd wasm-bindgen/crates/js-sys
|
||
cargo test --target wasm32-unknown-unknown
|
||
</code></pre>
|
||
<p>These tests are largely executed in Node.js right now via the
|
||
<a href="contributing/js-sys/../../wasm-bindgen-test/index.html"><code>wasm-bindgen-test</code> framework</a></p>
|
||
<h1><a class="header" href="#adding-support-for-more-javascript-global-apis" id="adding-support-for-more-javascript-global-apis">Adding Support for More JavaScript Global APIs</a></h1>
|
||
<p>As of 2018-09-24 we've <a href="https://github.com/rustwasm/wasm-bindgen/issues/275">added all APIs</a> in the current ECMAScript
|
||
standard (yay!). To that end you'll hopefully not find a missing API, but if you
|
||
do please feel free to file an issue!</p>
|
||
<p>We currently add new APIs added to ECMAScript that are in <a href="https://tc39.github.io/process-document/">TC39 stage 4</a>
|
||
to this crate. If there's a new API in stage 4, feel free to file an issue as
|
||
well!</p>
|
||
<h3><a class="header" href="#instructions-for-adding-an-api" id="instructions-for-adding-an-api">Instructions for adding an API</a></h3>
|
||
<ul>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Find the <code>wasm-bindgen</code> issue for the API you'd like to add. If this
|
||
doesn't exist, feel free to open one! Afterwards be sure to comment on the
|
||
issue to avoid duplication of work.</p>
|
||
</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Open the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects">MDN
|
||
page</a>
|
||
for the relevant JS API.</p>
|
||
</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Open <code>crates/js-sys/src/lib.rs</code> in your editor; this is the file where we
|
||
are implementing the bindings.</p>
|
||
</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Follow the instructions in the top of <code>crates/js-sys/src/lib.rs</code> about how
|
||
to add new bindings.</p>
|
||
</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Add a test for the new binding to <code>crates/js-sys/tests/wasm/MyType.rs</code></p>
|
||
</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Run the <a href="contributing/js-sys/testing.html">JS global API bindings tests</a></p>
|
||
</li>
|
||
<li><input disabled="" type="checkbox"/>
|
||
<p>Send a pull request!</p>
|
||
</li>
|
||
</ul>
|
||
<h1><a class="header" href="#web-sys" id="web-sys"><code>web-sys</code></a></h1>
|
||
<p>The <code>web-sys</code> crate provides raw bindings to all of the Web's APIs, and its
|
||
source lives at <code>wasm-bindgen/crates/web-sys</code>.</p>
|
||
<p>The <code>web-sys</code> crate is <strong>entirely</strong> mechanically generated inside <code>build.rs</code>
|
||
using <code>wasm-bindgen</code>'s WebIDL frontend and the WebIDL interface definitions for
|
||
Web APIs. This means that <code>web-sys</code> isn't always the most ergonomic crate to
|
||
use, but it's intended to provide verified and correct bindings to the web
|
||
platform, and then better interfaces can be iterated on crates.io!</p>
|
||
<p>Documentation for the published version of this crate is available on
|
||
<a href="https://docs.rs/web-sys">docs.rs</a> but you can also check out the <a href="https://rustwasm.github.io/wasm-bindgen/api/web_sys/">master branch
|
||
documentation</a> for the crate.</p>
|
||
<h1><a class="header" href="#web-sys-overview" id="web-sys-overview"><code>web-sys</code> Overview</a></h1>
|
||
<p>The <code>web-sys</code> crate has this file and directory layout:</p>
|
||
<pre><code class="language-text">.
|
||
├── build.rs
|
||
├── Cargo.toml
|
||
├── README.md
|
||
├── src
|
||
│ └── lib.rs
|
||
└── webidls
|
||
└── enabled
|
||
└── ...
|
||
</code></pre>
|
||
<h3><a class="header" href="#webidlsenabledwebidl" id="webidlsenabledwebidl"><code>webidls/enabled/*.webidl</code></a></h3>
|
||
<p>These are the WebIDL interfaces that we will actually generate bindings for (or
|
||
at least bindings for <em>some</em> of the things defined in these files).</p>
|
||
<h3><a class="header" href="#buildrs" id="buildrs"><code>build.rs</code></a></h3>
|
||
<p>The <code>build.rs</code> invokes <code>wasm-bindgen</code>'s WebIDL frontend on all the WebIDL files
|
||
in <code>webidls/enabled</code>. It writes the resulting bindings into the cargo build's
|
||
out directory.</p>
|
||
<h3><a class="header" href="#srclibrs-16" id="srclibrs-16"><code>src/lib.rs</code></a></h3>
|
||
<p>The only thing <code>src/lib.rs</code> does is include the bindings generated at compile
|
||
time in <code>build.rs</code>. Here is the whole <code>src/lib.rs</code> file:</p>
|
||
<pre><pre class="playpen"><code class="language-rust">
|
||
# #![allow(unused_variables)]
|
||
#fn main() {
|
||
//! Raw API bindings for Web APIs
|
||
//!
|
||
//! This is a procedurally generated crate from browser WebIDL which provides a
|
||
//! binding to all APIs that browser provide on the web.
|
||
//!
|
||
//! This crate by default contains very little when compiled as almost all of
|
||
//! its exposed APIs are gated by Cargo features. The exhaustive list of
|
||
//! features can be found in `crates/web-sys/Cargo.toml`, but the rule of thumb
|
||
//! for `web-sys` is that each type has its own cargo feature (named after the
|
||
//! type). Using an API requires enabling the features for all types used in the
|
||
//! API, and APIs should mention in the documentation what features they
|
||
//! require.
|
||
|
||
#![doc(html_root_url = "https://docs.rs/web-sys/0.2")]
|
||
#![allow(deprecated)]
|
||
|
||
mod features;
|
||
pub use features::*;
|
||
|
||
/// Getter for the `Window` object
|
||
///
|
||
/// [MDN Documentation]
|
||
///
|
||
/// *This API requires the following crate features to be activated: `Window`*
|
||
///
|
||
/// [MDN Documentation]: https://developer.mozilla.org/en-US/docs/Web/API/Window
|
||
#[cfg(feature = "Window")]
|
||
pub fn window() -> Option<Window> {
|
||
use wasm_bindgen::JsCast;
|
||
|
||
js_sys::global().dyn_into::<Window>().ok()
|
||
}
|
||
|
||
#}</code></pre></pre>
|
||
<h3><a class="header" href="#cargo-features" id="cargo-features">Cargo features</a></h3>
|
||
<p>When compiled the crate is almost empty by default, which probably isn't what
|
||
you want! Due to the very large number of APIs, this crate uses features to
|
||
enable portions of its API to reduce compile times. The list of features in
|
||
<code>Cargo.toml</code> all correspond to types in the generated functions. Enabling a
|
||
feature enables that type. All methods should indicate what features need to be
|
||
activated to use the method.</p>
|
||
<h1><a class="header" href="#testing-1" id="testing-1">Testing</a></h1>
|
||
<p>You can test the <code>web-sys</code> crate by running <code>cargo test</code> within the
|
||
<code>crates/web-sys</code> directory in the <code>wasm-bindgen</code> repository:</p>
|
||
<pre><code class="language-sh">cd wasm-bindgen/crates/web-sys
|
||
cargo test --target wasm32-unknown-unknown --all-features
|
||
</code></pre>
|
||
<p>The Wasm tests all run within a headless browser. See <a href="https://github.com/rustwasm/wasm-bindgen/blob/master/crates/test/README.md">the <code>wasm-bindgen-test</code>
|
||
crate's
|
||
<code>README.md</code></a>
|
||
for details and configuring which headless browser is used.</p>
|
||
<h1><a class="header" href="#logging" id="logging">Logging</a></h1>
|
||
<p>The <code>wasm_bindgen_webidl</code> crate (used by <code>web-sys</code>'s <code>build.rs</code>) uses
|
||
<a href="https://crates.io/crates/env_logger"><code>env_logger</code></a> for logging, which can be enabled by setting the
|
||
<code>RUST_LOG=wasm_bindgen_webidl</code> environment variable while building the <code>web-sys</code>
|
||
crate.</p>
|
||
<p>Make sure to enable "very verbose" output during <code>cargo build</code> to see these logs
|
||
within <code>web-sys</code>'s build script output.</p>
|
||
<pre><code class="language-sh">cd crates/web-sys
|
||
RUST_LOG=wasm_bindgen_webidl cargo build -vv
|
||
</code></pre>
|
||
<p>If <code>wasm_bindgen_webidl</code> encounters WebIDL constructs that it doesn't know how
|
||
to translate into <code>wasm-bindgen</code> AST items, it will emit warn-level logs.</p>
|
||
<pre><code>WARN 2018-07-06T18:21:49Z: wasm_bindgen_webidl: Unsupported WebIDL interface: ...
|
||
</code></pre>
|
||
<h1><a class="header" href="#supporting-more-web-apis-in-web-sys" id="supporting-more-web-apis-in-web-sys">Supporting More Web APIs in <code>web-sys</code></a></h1>
|
||
<ol>
|
||
<li>
|
||
<p><input type="checkbox"/> Ensure that the <code>.webidl</code> file describing the
|
||
interface exists somewhere within the <code>crates/web-sys/webidls/enabled</code>
|
||
directory.</p>
|
||
<p>First, check to see whether we have the WebIDL definition file for
|
||
your API:</p>
|
||
<pre><code class="language-sh">grep -rn MyWebApi crates/web-sys/webidls
|
||
</code></pre>
|
||
<ul>
|
||
<li>
|
||
<p>If your interface is defined in a <code>.webidl</code> file that is inside the
|
||
<code>crates/web-sys/webidls/enabled</code> directory, skip to step (3).</p>
|
||
</li>
|
||
<li>
|
||
<p>If your interface isn't defined in any file yet, find the WebIDL definition
|
||
in the relevant standard and add it as a new <code>.webidl</code> file in
|
||
<code>crates/web-sys/webidls/enabled</code>. Make sure that it is a standard Web API!
|
||
We don't want to add non-standard APIs to this crate.</p>
|
||
</li>
|
||
<li>
|
||
<p>If your interface is defined in a <code>.webidl</code> file within any of the
|
||
<code>crates/web-sys/webidls/unavailable_*</code> directories, you need to move it into
|
||
<code>crates/web-sys/webidls/enabled</code>, e.g.:</p>
|
||
<pre><code class="language-sh">cd crates/web-sys
|
||
git mv webidls/unavailable_enum_ident/MyWebApi.webidl webidls/enabled/MyWebApi.webidl
|
||
</code></pre>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> Verify that the <code>web-sys</code> crate still builds and
|
||
that its tests still pass with the new <code>.webidl</code> file enabled:</p>
|
||
<pre><code class="language-sh">cd crates/web-sys
|
||
cargo build
|
||
cargo test
|
||
</code></pre>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> Verify that bindings are being generated for your new
|
||
API by generating the documentation and searching for the new API in it:</p>
|
||
<pre><code class="language-sh">cd crates/web-sys
|
||
cargo doc --open
|
||
# search for the new API in the opened docs
|
||
</code></pre>
|
||
<ul>
|
||
<li>
|
||
<p><input type="checkbox"/> If the new API is <strong>not</strong> showing up in the docs,
|
||
rebuild the <code>web-sys</code> crate <a href="contributing/web-sys/logging.html">with logging enabled</a>
|
||
and look for warning messages that mention your new API. Figure out why
|
||
bindings weren't generated and then add support to <code>wasm_bindgen_webidl</code> for
|
||
whatever is needed to generate your API's bindings.</p>
|
||
<blockquote>
|
||
<p>You might find it helpful to view the generated rust bindings, to see if
|
||
they are what you would expect. The file will be located at
|
||
<code>target/wasm32-unknown-unknown/debug/build/web-sys-xxx/out/bindings.rs</code>,
|
||
where <code>xxx</code> is a combinations of numbers and letters that represents your
|
||
build. This file is pretty unintelligable until you run <code>rustfmt</code> on it, like
|
||
<code>rustfmt target/wasm32-unknown-unknown/debug/build/web-sys-xxx/out/bindings.rs</code>.</p>
|
||
</blockquote>
|
||
<blockquote>
|
||
<p>There are commented out lines in <code>web-sys/build.rs</code> that run rustfmt as part of
|
||
the build process, and this can be very helpful for debugging as any error
|
||
messages with inline code will display it in a readable format.</p>
|
||
</blockquote>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> Add tests for as many of the features in the WebIDL file
|
||
as possible to <code>crates/web-sys/tests/all/</code>. See the
|
||
<a href="contributing/web-sys/testing.html"><code>web-sys</code> testing documentation</a> for details.</p>
|
||
<blockquote>
|
||
<p><strong>Note</strong>: Start here at <strong>4</strong> if the WebIDL has already been added but doesn't have
|
||
full test coverage, then go back to <strong>3</strong> if you find any problems.</p>
|
||
</blockquote>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> If all entities in the WebIDL file have full test coverage,
|
||
mark the WebIDL script in the <code>README.md</code> file as complete by changing <code>[ ]</code> to <code>[x]</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> Send a pull request! 😊</p>
|
||
</li>
|
||
</ol>
|
||
<h1><a class="header" href="#publishing-new-wasm-bindgen-releases" id="publishing-new-wasm-bindgen-releases">Publishing New <code>wasm-bindgen</code> Releases</a></h1>
|
||
<ol>
|
||
<li>
|
||
<p><input type="checkbox"/> Compile the <code>publish.rs</code> script:</p>
|
||
<pre><code>rustc publish.rs
|
||
</code></pre>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> Bump every crate's minor version:</p>
|
||
<pre><code># Make sure you are in the root of the wasm-bindgen repo!
|
||
./publish bump
|
||
</code></pre>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> Send a pull request for the version bump.</p>
|
||
</li>
|
||
<li>
|
||
<p><input type="checkbox"/> After the pull request's CI is green and it has been
|
||
merged, publish to cargo:</p>
|
||
<pre><code># Make sure you are in the root of the wasm-bindgen repo!
|
||
./publish publish
|
||
</code></pre>
|
||
</li>
|
||
</ol>
|
||
<h1><a class="header" href="#team" id="team">Team</a></h1>
|
||
<p><code>wasm-bindgen</code> follows the <a href="https://github.com/rustwasm/team/blob/master/GOVERNANCE.md#repositories"><code>rustwasm</code> organization's governance described
|
||
here</a>:</p>
|
||
<ul>
|
||
<li>
|
||
<p>All pull requests (including those made by a team member) must be approved by
|
||
at least one other team member.</p>
|
||
</li>
|
||
<li>
|
||
<p>Larger, more nuanced decisions about design, architecture, breaking changes,
|
||
trade offs, etc are made by team consensus.</p>
|
||
</li>
|
||
</ul>
|
||
<h2><a class="header" href="#members" id="members">Members</a></h2>
|
||
<style>
|
||
img {
|
||
max-width: 117px;
|
||
max-height: 117px;
|
||
}
|
||
</style>
|
||
<p>| <a href="https://github.com/alexcrichton"><img src="https://github.com/alexcrichton.png?size=117" alt="" /></a> | <a href="https://github.com/fitzgen"><img src="https://github.com/fitzgen.png?size=117" alt="" /></a> | <a href="https://github.com/spastorino"><img src="https://github.com/spastorino.png?size=117" alt="" /></a> | <a href="https://github.com/ohanar"><img src="https://github.com/ohanar.png?size=117" alt="" /></a> | <a href="https://github.com/jonathan-s"><img src="https://github.com/jonathan-s.png?size=117" alt="" /></a> |
|
||
|:---:|:---:|:---:|:---:|
|
||
| <a href="https://github.com/alexcrichton"><code>alexcrichton</code></a> | <a href="https://github.com/fitzgen"><code>fitzgen</code></a> | <a href="https://github.com/spastorino"><code>spastorino</code></a> | <a href="https://github.com/ohanar"><code>ohanar</code></a> | <a href="https://github.com/jonathan-s"><code>jonathan-s</code></a> |
|
||
| <a href="https://github.com/sendilkumarn"><img src="https://github.com/sendilkumarn.png?size=117" alt="" /></a> | <a href="https://github.com/belfz"><img src="https://github.com/belfz.png?size=117" alt="" /></a> | <a href="https://github.com/afdw"><img src="https://github.com/afdw.png?size=117" alt="" /></a> | | |
|
||
| <a href="https://github.com/sendilkumarn"><code>sendilkumarn</code></a> | <a href="https://github.com/belfz"><code>belfz</code></a> | <a href="https://github.com/afdw"><code>afdw</code></a> | | |</p>
|
||
|
||
</main>
|
||
|
||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||
<!-- Mobile navigation buttons -->
|
||
|
||
|
||
|
||
|
||
<div style="clear: both"></div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||
|
||
|
||
|
||
</nav>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||
|
||
|
||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||
|
||
<!-- Custom JS scripts -->
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript">
|
||
window.addEventListener('load', function() {
|
||
window.setTimeout(window.print, 100);
|
||
});
|
||
</script>
|
||
|
||
|
||
|
||
</body>
|
||
</html>
|