WebAssembly и Rust: создание высокопроизводительных веб-приложений

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в 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-модуль для преобразования изображения

Блоги

Рекомендаций по книгам