Это сообщение впервые появилось на WebAssemblyMan.com: Rust WebAssembly Hello World.

В этой статье мы собираемся создать программу WebAssembly Hello World на Rust. Вы можете подумать, что учебник по WebAssembly Hello World можно найти почти везде, в том числе на веб-сайте Mozilla и rustwasm.github.io. Так зачем вам читать эту статью?

Эта статья отличается тем, что мы выходим не только на то, чтобы заставить работать программу Hello World. Мы будем копать глубже, чтобы понять коды, генерируемые wasm-bindgen, и то, как все они объединяются, чтобы помочь нам в разработке. WebAssembly Binary Toolkit также будет использоваться для изучения сгенерированного кода wasm. Это даст нам хорошее понимание Rust WebAssembly и хорошую основу для начала. Также быстро становится ясно, почему приложения Rust WebAssembly меньше и более оптимизированы по сравнению с другими фреймворками с Garbage Collector, и почему это лучший вариант для разработчиков с существующими веб-приложениями или веб-сайтами JavaScript. Такие вещи, как web-sys (необработанные привязки API для веб-APIS) и WASI (системный интерфейс WebAssembly), также становятся более понятными.

Предварительные требования

Учебник Hello World по Rust WebAssembly

1. Установите wasm-bindgen. Есть и другие способы создать программу Hello World без wasm-bindgen. Но в этом руководстве мы собираемся использовать его, поскольку он необходим при разработке Rust WebAssembly.

грузовая установка wasm-bindgen-cli

Rust WebAssembly позволяет хирургическим путем вставлять модули WebAssembly в существующие приложения JavaScript, особенно в пути кода, критичные для производительности. Вы можете думать о wasm-bindgen как об инструменте, который помогает вам в этом, создавая клей и привязки для эффективного взаимодействия между JavaScript и WebAssembly.

2. Далее нам нужно создать новый проект Rust WebAssembly. Введите следующую команду

cargo new helloworld — lib

Обратите внимание на двойной дефис перед «lib».

Вы должны увидеть следующее сообщение. Создан новый пакет helloworld с файлами вашего проекта.

Создан пакет библиотеки `helloworld`

3. С помощью текстового редактора откройте Cargo.toml. Вы должны увидеть следующее

[package]
name = "helloworld"
version = "0.1.0"
authors = ["djembe-waka "]
edition = "2018"
[dependencies]

Измените его на следующее:

[package]
name = "helloworld"
version = "0.1.0"
authors = ["djembe-waka "]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"

Индикатор «cdylib» заставляет этот проект Rust предоставлять динамическую библиотеку в стиле C, которая используется компоновщиками для создания нашего модуля WebAssembly. Как описано выше, wasm-bindgen включает или упрощает высокоуровневые взаимодействия между модулями wasm и JavaScript.

4. С помощью текстового редактора откройте «src / lib.rs». Измените его на следующее:


extern crate wasm_bindgen; 
use wasm_bindgen::prelude::*; 
// Import 'window.alert' 
#[wasm_bindgen] 
extern "C" { 
	fn alert(s: &str); 
} 
// Export a 'helloworld' function 
#[wasm_bindgen] 
pub fn helloworld(name: &str) { 
	alert(&format!("Hello World : {}!", name)); 
}

В приведенном выше коде wasm_bindgen позволяет нам импортировать элементы JavaScript («предупреждение») в Rust и экспортировать элементы Rust («helloworld») в JavaScript.

5. Скомпилируйте код. В командной строке введите следующее:

сборка груза - цель wasm32-unknown-unknown

Обратите внимание на двойное тире перед словом «цель». Если «wasm32-unknown-unknown target» не установлен, введите следующее, чтобы установить его.

цель rustup добавить wasm32-unknown-unknown

6. Настройте веб-сервер для запуска нашей программы.

Нам нужен веб-сервер для тестирования нашей программы WebAssembly «Hello World». Мы собираемся использовать Webpack, но вы также можете использовать другой инструмент или веб-сервер, которые вам удобны. Нам нужно создать 3 файла: index.js, package.json и webpack.config.js. Однако мы не будем вдаваться в подробности этих файлов, так как сосредоточимся на кодах, сгенерированных wasm-bindgen.

index.js

const rust = import('./pkg/helloworld');
rust
  .then(m => m.helloworld('World!'))
  .catch(console.error);

package.json

{
  "scripts": {
    "build": "webpack",
    "serve": "webpack-dev-server"
  },
  "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "0.4.2",
    "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"
  }
}

webpack.config.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'
};

Вам могут потребоваться следующие команды, если некоторые пакеты недоступны в вашей системе.

  • npm установить веб-пакет - save-dev
  • npm установить webpack-cli - save-dev
  • npm установить webpack-dev-server - save-dev
  • npm установить плагин html-webpack - save-dev
  • npm install @ wasm-tool / wasm-pack-plugin - save-dev
  • npm установить кодировку текста - save-dev

Обратите внимание на двойной дефис перед «save-dev».

7. Соберите программу, выполнив следующую команду:

npm запустить сборку

8. Запустите программу Hello World.

npm run serve

Перейдите в браузере к «localhost: 8080» и увидите «Hello World!» отображается предупреждение.

8. Теперь, когда у нас запущена программа Hello World, мы хотим копнуть глубже. Перейдите в папку «pkg» и убедитесь, что следующие файлы автоматически создаются для вас в процессе сборки.

  • «Helloworld_bg.wasm»
  • «Helloworld.js»
  • «Helloworld.d.ts»
  • «Package.json»

Файлы также можно сгенерировать вручную с помощью следующей команды wasm-bindgen cli.

wasm-bindgen target / wasm32-unknown-unknown / debug / helloworld.wasm - out-dir ./pkg

Обратите внимание на двойное тире перед словом «out-dir».

9. Первое, что нам нужно сделать, это понять, что происходит, когда мы запускаем программу Hello World в браузере. Ниже показана последовательность вызовов функций (или, точнее, файлов, содержащих функции), которые происходят, когда мы переходим к точке «localhost: 8080» в браузере.

index.js - ›helloworld.js -› helloworld_bg.wasm

index.js

const rust = import('./pkg/helloworld');
rust
  .then(m => m.helloworld('World!'))
  .catch(console.error);

Index.js импортирует пакет helloworld.js и вызывает в нем функцию helloworld.

helloworld.js


export function helloworld(name) {
    const ptr0 = passStringToWasm(name);
    const len0 = WASM_VECTOR_LEN;
    try {
        return wasm.helloworld(ptr0, len0);

    } finally {
        wasm.__wbindgen_free(ptr0, len0 * 1);

    }
}

Файл helloworld.js автоматически создается wasm-bindgen и содержит связующее звено JavaScript для импорта функций DOM и JavaScript в Rust. Он также предоставляет API для созданных функций WebAssembly для JavaScript.

Rust WebAssembly фокусируется на интеграции WebAssembly с существующими приложениями JavaScript. Для этого нам нужно передавать различные значения, объекты или структуры из JavaScript в функции WebAssembly и из них. Это непросто, поскольку необходимо согласовать разные типы объектов двух разных систем. Что еще хуже, WebAssembly в настоящее время поддерживает только целые числа и числа с плавающей запятой, но не строки. Это означает, что вы не можете просто передать строку в функцию WebAssembly.

Чтобы передать строку в WebAssembly, вам необходимо преобразовать строку в числа (обратите внимание на TextEncoderAPI, указанный в webpack.config.js), поместить числа в область памяти WebAssembly и, наконец, вернуть указатель строки в функцию WebAssembly, чтобы что вы можете использовать его в JavaScript. В конце вам нужно будет освободить пространство памяти WebAssembly, используемое строкой.

Если вы посмотрите на приведенные выше коды JavaScript, это именно то, что было выполнено автоматически. Функция «helloworld» сначала вызывает «passStringToWasm». Эта функция создает некоторое пространство памяти в WebAssembly, преобразует вашу строку в числа, записывает числа в область памяти и возвращает указатель на строку. Затем указатель передается на «wasm.helloworld» для выполнения вашего «предупреждения» JavaScript. Наконец, wasm .__ wbindgen_free освобождает память. Если вы просто передаете простую строку, вы можете сделать это самостоятельно, но подумайте, когда у вас есть более сложные объекты и структуры, усилия быстро станут нетривиальными. Это иллюстрирует важность wasm-bindgen в разработке Rust WebAssembly.

10. На предыдущем шаге мы отметили, что файл «helloworld.js» создается с помощью wasm-bindgen, а функции, сгенерированные в этом файле, вызывают наши сгенерированные коды WebAssembly в «helloworld_bg.wasm». По сути, helloworld.js действует как мост между другими сценариями JavaScripts, такими как index.js, и созданной WebAssembly helloworld_bg.wasm.

Мы можем продолжить изучение «helloworld_bg.wasm», введя следующую команду:

wasm2wat helloworld_bg.wasm ›helloworld.txt

Эта команда использует WebAssemblyBinary Toolkit для перевода WebAssembly в текстовый формат WebAssembly и сохраняет его в файл helloworld.txt. Откройте «helloworld.txt» в текстовом редакторе и найдите функцию «$ helloworld». Это сгенерированная функция WebAssembly нашей функции helloworld, которую мы определили в src / lib.rs.

Найдите в helloworld.txt следующую строку:


(export "helloworld" (func $helloworld))

Эта строка экспортирует wasm-функцию helloworld для вызова хостом. Мы вызываем эту функцию wasm через wasm.helloworld в helloworld.js.
Затем найдите следующую строку:


(import "./helloworld.js" "__wbg_alert_7e2b9b91978b2246" (func $__wbg_alert_7e2b9b91978b2246 (type 2)))

Это соответствует следующей функции JavaScript, созданной в helloworld.js.


export const __wbg_alert_7e2b9b91978b2246 = function(arg0, arg1)

Это та часть, где wasm-bindgen предоставляет связующее звено, которое поможет вам использовать функции JavaScript или DOM в WebAssembly. Теперь мы понимаем, как wasm-bindgen помогает нам облегчить взаимодействие, импортируя элементы JavaScript и экспортируя элементы WebAssembly.

11. Наконец, давайте взглянем на другие файлы, созданные wasm-bindgen.

«Helloworld.d.ts»

Этот файл .d.ts содержит объявления типа TypeScript для связки JavaScript и полезен, если ваше существующее приложение JavaScript использует TypeScript. Вы можете проверить тип вызовов функций WebAssembly или настроить автоматическое завершение в среде IDE. Если вы не используете TypeScript, игнорируйте этот файл.

«Package.json»

Файл package.json содержит метаданные о сгенерированном пакете JavaScript и WebAssembly. Он автоматически заполняет все зависимости npm из вашего кода Rust и позволяет публиковать в npm.

Какое дальнейшее понимание мы можем извлечь из вышеизложенного?

Взгляните еще раз на следующий код:

helloworld.js


export function helloworld(name) {
    const ptr0 = passStringToWasm(name);
    const len0 = WASM_VECTOR_LEN;
    try {
        return wasm.helloworld(ptr0, len0);

    } finally {
        wasm.__wbindgen_free(ptr0, len0 * 1);

    }
}

Код для выделения и освобождения памяти выполняется за вас. Нет необходимости в сборщике мусора или полном механизме инфраструктуры, что делает приложения или модули WebAssembly, написанные на Rust небольшими и оптимизированными. Другие языки, которым требуется сборщик мусора, должны будут включать код wasm для своего базового движка фреймворка. Итак, независимо от того, насколько они оптимизированы, их размер не будет меньше того, что предоставляет Rust. Это делает Rust WebAssembly хорошим выбором, если вам нужно интегрировать или внедрять небольшие модули WebAssembly в веб-приложения JavaScript.

Другие вещи, на которые стоит обратить внимание помимо Hello World

веб-система

С wasm-bindgen мы можем вызывать функцию JavaScript в Rust WebAssembly, используя «extern». Помните следующее из «src / lib.rs»:

#[wasm_bindgen] 
extern "C" { 
	fn alert(s: &str); 
}

В Интернете есть огромное количество API-интерфейсов, от управления DOM до WebGL и веб-аудио. Так что, если наша программа Rust WebAssembly будет расти, и нам нужно будет делать много разных вызовов веб-API, нам нужно будет потратить время на написание большого количества «внешнего» кода. web-sys служит интерфейсом для wasm-bindgen, обеспечивая необработанные привязки ко всем веб-интерфейсам API. Это означает, что если вы используете web-sys, вы можете сэкономить время, избавившись от необходимости писать внешний код.

WASI (интерфейс системы WebAssembly)

Это попытка стандартизации, направленная на то, чтобы вывести WebAssembly за пределы браузера. Чтобы запустить WebAssembly на компьютере или в ОС (операционной системе) вне браузера, нам может потребоваться доступ к системным ресурсам. Эта стандартизация определяет, как вы можете получить доступ к этим системным ресурсам безопасным и в то же время переносимым способом на разных архитектурах компьютеров. В настоящее время уже существует две реализации: Wasmtime от Mozilla и Lucet от Fastly. И мы должны спросить себя, станет ли WASM + WASI следующим докером?

Принесено вам с помощью WebAssemblyMan.com: Man page of WebAssembly.