Как реализовывается WebAssembly в Rust: кратко
Привет, Хабр!
Rust - язык, который за последние годы завоевал сердца разработчиков своей надежностью, а в особенности безопасностью. WebAssembly в свою очередь позволяет нам перенести производительность и мощь нативных приложений в браузер. Вместе эти инструменты позволяют сделать веб-приложения с высокой скоростью, ну и кончечно - высокой безопасностью.
з.ы: предполагается, что читатель знает основы webassembly и про что-то из rust
Реализация
wasm-bindgen
wasm-bindgen
– это библиотека и инструмент командной строки, который облегчает взаимодействие между кодом на Rust и JavaScript, позволяет вызывать функции JavaScript из Rust и наоборот, а также работать с различными типами данных.
Можно вызывать функции JavaScript прямо из Rust. Это позволяет использовать возможности браузера и веб-API, не покидая экосистему Rust.
wasm-bindgen позволяет экспортировать функции Rust в WebAssembly, которые затем могут быть вызваны из JavaScript, также он облегчает обмен сложными типами данных между Rust и JavaScript, например, строками, структурами и даже целыми классами.
Допустим, у нас есть следующая функция JavaScript, которая выводит сообщение в консоль:
function greet(name) {
console.log(`Hello, ${name}!`);
}
Вызываем эту функцию из Rust следующим образом:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn greet(name: &str);
}
#[wasm_bindgen(start)]
pub fn run() {
greet("World");
}
Теперь сделаем наоборот. Напишем функцию на Rust и вызовем её из JavaScript:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(x: i32, y: i32) -> i32 {
x + y
}
Теперь мы можем вызвать эту функцию из JavaScript:
import { add } from './my_module';
console.log(add(5, 7)); // Выведет 12
wasm-bindgen
также позволяет работать со структурами Rust в JavaScript. Например:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Point {
x: i32,
y: i32,
}
#[wasm_bindgen]
impl Point {
pub fn new(x: i32, y: i32) -> Point {
Point { x, y }
}
pub fn x(&self) -> i32 {
self.x
}
pub fn y(&self) -> i32 {
self.y
}
}
В JavaScript мы можем создать и использовать объект Point
так:
import { Point } from './my_module';
const p = Point.new(2, 3);
console.log(p.x()); // Выведет 2
console.log(p.y()); // Выведет 3
wasm-pack
wasm-pack
– это инструмент, предназначенный для сборки Rust-кода в WebAssembly и управления всем процессом от начала до конца.
wasm-pack
компилирует Rust-код в WebAssembly, оптимизируя его для использования в вебе. После сборки wasm-pack
помогает упаковать проект в формат, совместимый с npm, что упрощает его распространение и использование.
wasm-pack
поддерживает тестирование кода на WebAssembly.
Установку wasm-pack
можно сделать через cargo
:
cargo install wasm-pack
Затем создаем новый проект с использованием cargo
:
cargo new --lib my_wasm_project
cd my_wasm_project
В файле Cargo.toml
проекта указывается зависимостьwasm-bindgen
, так как wasm-pack
работает в тандеме с ним:
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
В файле src/lib.rs
создадим простую функцию:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
Теперь можно собрать проект с помощью wasm-pack
:
wasm-pack build
Команда создаст папку pkg
, в которой будет находиться скомпилированный WebAssembly код и автоматически сгенерированные JavaScript-обвязки.
После успешной сборки можно опубликовать свой пакет в npm:
wasm-pack publish
Теперь WebAssembly-модуль готов к использованию в JavaScript-проекте. Можно импортировать и использовать функцию greet
в JavaScript-коде:
import { greet } from 'my_wasm_project';
console.log(greet('World')); // Выведет "Hello, World!"
cargo-web
cargo-web
предоставляет простой в использовании локальный веб-сервер, который автоматически перезагружает ваше приложение при изменении кода.
Для начала установим cargo-web
:
cargo install cargo-web
Затем создадим новый проект:
cargo new --lib my_web_project
cd my_web_project
В Cargo.toml
, укажем тип сборки и добавим зависимости:
[package]
name = "my_project"
version = "1"
edition = "2"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
В src/lib.rs
добавим простую функцию, которая будет использоваться в веб-приложении:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn say_hello() -> String {
"Hello from Rust!".to_string()
}
Теперь можно запустить локальный веб-сервер с помощью cargo-web
:
cargo web start
Эта команда запустит веб-сервер и откроет приложение в браузере. Любые изменения в коде будут автоматически отображаться.
Чтобы использовать функцию say_hello
в веб-странице, создадим HTML-файл:
<!DOCTYPE html>
<html>
<head>
<title>Rust WebAssembly App</title>
<script type="module">
import init, { say_hello } from './my_web_project.js';
async function run() {
await init();
alert(say_hello());
}
run();
</script>
</head>
<body>
<h1>Hello from Rust and WebAssembly!</h1>
</body>
</html>
Этот код загрузит и инициализирует сгенерированный WebAssembly-модуль, а затем вызовет функцию say_hello
.
Rust имеет хорошие инструменты для реализации WASM. Спасибо за прочтение статьи
В продолжение темы хочу напомнить про бесплатные вебинары от экспертов рынка про безопасный unsafe Rust и про то, как Rust побуждает использовать композицию.
А больше курсов от экспертов OTUS можно найти в полном каталоге.