WebAssembly est, avant tout, un standard pensé dans l’optique d’améliorer les performances de JS et d’établir un socle commun qui pourra, à l’avenir, être exploité par une variété de langages. Les premiers à communiquer avec wasm étant C, C++ ainsi que Rust.
Dans ce billet, je m’efforcerai de détailler les (chouettes) solutions mises en place pour faciliter cette intégration. C’est parti!
Un peu de contexte...
Actuellement, il existe trois principaux outils supportant la compilation, vers wasm, des langages dont je parlais plus haut:
Notez que Emscripten et Binaryen sont axés sur C et C++. Ça ne signifie, cependant, pas qu’ils ne supporteront jamais d’autres langages et c’est d’ailleurs l’un des objectifs partagés par wasm-bindgen qui, à terme, ne devra plus être directement lié au langage Rust.
Sa structure
En réalité, le projet est composé de deux éléments:
Petit prix à payer en revanche, ces fonctionnalités ne sont disponibles qu’en compilant avec la nightly de Rust.
Son fonctionnement
Les présentations ainsi faites, passons à des choses plus intéressantes!
L’attribut wasm-bindgen
Du côté de notre crate, #[wasm-bindgen] servira à "marquer" les services, structures et implémentations (impl) puis préparera à l’exportation tout ce petit monde sous la forme d’un module ECMAScript, facilitant, ensuite, largement leur importation dans le code JavaScript censé les accueillir.
400208
Exporter des composants écrits en Rust vers JS
Prenons, dans un premier temps, les exemples originaux.
Côté Rust, dans lib.rs:
// Current prelude for using `wasm_bindgen`, and this'll get smaller over time!
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
// Here we're importing the `alert` function from the browser, using
// `#` to generate correct wrappers.
#
extern {
fn alert(s: &str);
}
// Here we're exporting a function called `greet` which will display a greeting
// for `name` through a dialog.
#
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
Côté JavaScript:
const { greet } = wasm_bindgen;
function runApp() {
greet('World');
}
// Load and instantiate the wasm file, and we specify the source of the wasm
// file here. Once the returned promise is resolved we're ready to go and
// use our imports.
wasm_bindgen('../out/main_bg.wasm').then(runApp).catch(console.error);
Vous pouvez remarquer à la ligne 9 que le bloc extern {}, couramment utilisé avec la FFI, dispose de la même fonction qu’en temps normal: déclarer les signatures des services que nous tentons d’utiliser à partir d’une ABI connue. Ici, nous souhaitons user de la méthode window.alert() que nous propose le navigateur.
Maintenant, nous pourrions toujours réadapter cet exemple pour illustrer ce qui a été dit juste avant: il nous est possible d’exporter des structures et l’implémentation de leurs services.
Côté Rust, dans lib.rs:
#
extern {
fn alert(s: &str);
}
#
pub struct Foo;
#
impl Foo {
pub fn new() -> Foo {
Foo
}
pub fn greet(&self, name: &str) {
alert(&format!("Hello, {}!", name));
}
}
Côté JavaScript:
/*
On récupère la structure exactement comme
on pourrait le faire avec un module ECMAScript
écrit en JavaScript.
*/
const { Foo } = wasm_bindgen;
function runApp() {
Foo.new().greet('World');
}
wasm_bindgen('../out/main_bg.wasm').then(runApp).catch(console.error);
Exporter des composants écrits en JS vers Rust
# dispose de trois paramètres (dont un variable):
Ce trio vous permet d’importer des structures initialement écrites en JS.
Côté Rust:
// On renseigne le nom du module.
#[wasm_bindgen(module = "main")]
extern {
fn alert(s: &str);
// Ici, on se sert du mot-clé `type`
// pour préciser que l'on souhaite importer
// la structure `MyJavaScriptObject`.
type MyJavaScriptObject;
// On définit la signature du constructeur.
#[wasm_bindgen(constructor)]
fn new() -> MyJavaScriptObject;
// On définit l'identificateur et la signature
// de la méthode d'instance.
#[wasm_bindgen(method)]
fn say_hello(this: &MyJavaScriptObject) -> ();
}
#
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
MyJavaScriptObject::new().say_hello();
}
Côté JavaScript:
export class MyJavaScriptObject {
constructor() {
this.greetings = "Hi, I'm a JavaScript object!";
}
say_hello() {
console.log(this.greetings);
}
}
Mon avis
J’aimerais faire un retour qui servira de conclusion à ce petit billet, si ça peut aider certains développeurs à situer WASM/Rust en termes de maturité.
Nous avons une vision basique de ce qu’un développeur utilisant wasm est censé devoir faire pour lier JS à un module wasm. Rien de très compliqué, en somme, mais les possibilités me semblent encore limitées.
Ajoutons à cela que la compréhension même du langage n’est pas encore très bonne. Les lifetimes ne sont pas supportées, les constantes ne peuvent être exportées et les wrappers tels que Option et Result ne sont pas disponibles. En bref, y’a du boulot. :D
Source
Voir aussi
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.