Введение в WebAssembly и Rust
Современные веб-приложения требуют всё большей производительности. JavaScript, при всех его достоинствах, не всегда справляется с вычислительно сложными задачами — обработкой изображений, 3D-рендерингом, криптографией или симуляциями. Здесь на помощь приходит WebAssembly (WASM) — бинарный формат инструкций, который выполняется в браузере с почти нативной скоростью.
Язык Rust стал одним из лучших кандидатов для написания WASM-модулей: он безопасен, быстр и не имеет сборщика мусора. В этой статье мы разберём, как компилировать Rust-код в WebAssembly, связывать его с JavaScript и создавать реальные высокопроизводительные веб-приложения.
Настройка окружения для Rust + WASM
Прежде чем писать код, установите необходимые инструменты:
- Rust — установите через
rustup(официальный инсталлятор). - wasm-pack — основной инструмент для сборки WASM-пакетов.
- wasm-bindgen — библиотека для связывания Rust и JavaScript.
- Node.js и npm — для тестирования и сборки фронтенда.
# Установка wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# Создание нового проекта
cargo new --lib wasm-example
cd wasm-example
После создания проекта откройте Cargo.toml и добавьте зависимости:
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
Пишем первую функцию на Rust для WebAssembly
Создадим простую функцию, которая складывает два числа и возвращает результат. В src/lib.rs напишем:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Атрибут #[wasm_bindgen] автоматически генерирует JavaScript-обёртку для этой функции. Теперь соберём проект:
wasm-pack build --target web
После сборки в папке pkg появятся файлы: .wasm, .js (обёртка) и .d.ts (типы TypeScript).
Интеграция с HTML и JavaScript
Создадим простую HTML-страницу для теста:
<!DOCTYPE html>
<html>
<head>
<script type="module">
import init, { add } from './pkg/wasm_example.js';
async function run() {
await init();
console.log('2 + 3 =', add(2, 3)); // 5
}
run();
</script>
</head>
<body>
<h1>Rust + WebAssembly работает!</h1>
</body>
</html>
Откройте файл через локальный сервер (например, python3 -m http.server) — в консоли браузера увидите результат 5.
Работа со строками и сложными типами данных
WebAssembly оперирует только числами. Для работы со строками, массивами и объектами используется wasm-bindgen, который автоматически конвертирует типы.
Пример: функция, возвращающая строку
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Привет, {}! Ты используешь Rust + WASM.", name)
}
В JavaScript:
import init, { greet } from './pkg/wasm_example.js';
await init();
console.log(greet("Алексей"));
// "Привет, Алексей! Ты используешь Rust + WASM."
Работа с массивами (Vec)
Для передачи массивов используйте Vec<T>. Пример — функция, удваивающая все элементы:
#[wasm_bindgen]
pub fn double_numbers(arr: Vec<i32>) -> Vec<i32> {
arr.iter().map(|x| x * 2).collect()
}
JavaScript-код:
const result = double_numbers([1, 2, 3, 4]);
console.log(result); // [2, 4, 6, 8]
Оптимизация производительности и отладка
Одна из главных целей WASM — производительность. Рассмотрим ключевые приёмы оптимизации.
Избегайте лишних копирований
При передаче больших массивов между JS и WASM данные копируются. Используйте прямой доступ к памяти через js_sys::WebAssembly::Memory или библиотеку wasm-bindgen с атрибутом #[wasm_bindgen] для передачи слайсов напрямую.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn process_data(ptr: *mut u8, len: usize) {
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
// Работаем с памятью без копирования
for byte in slice.iter_mut() {
*byte = byte.wrapping_add(1);
}
}
Используйте release-сборку
По умолчанию wasm-pack build собирает в debug-режиме. Для продакшена используйте флаг --release:
wasm-pack build --target web --release
Это включает оптимизации LLVM и уменьшает размер .wasm-файла.
Профилирование
Используйте встроенные инструменты браузера (Chrome DevTools → Performance) для анализа времени выполнения WASM-функций. Также можно добавить логирование через web-sys:
use wasm_bindgen::prelude::*;
use web_sys::console;
#[wasm_bindgen]
pub fn log_message(msg: &str) {
console::log_1(&msg.into());
}
Реальный пример: обработка изображений
Создадим WASM-модуль для преобразования изображения