Найти в Дзене
Один Rust не п...Rust

Rust с WebAssembly

t.me/oneRustnoqRust
- Получить представление о WebAssembly. - Написать простой код CMS и мобильного приложения. Узнать что такое: Зачем Вам это уметь? : Написание CMS (системы управления контентом) в Rust с WebAssembly будет включать компиляцию кода Rust в WebAssembly, а затем использование его в веб-приложении. Вот пример того, как вы можете начать структурировать простую CMS в Rust с помощью WebAssembly:
пример простой структуры Page, которая представляет страницу контента в CMS: #[derive(Debug)] pub struct Page { title: String, content: String, } impl Page { pub fn new(title: String, content: String) -> Self { Self { title, content } } pub fn title(&self) -> &str { &self.title } pub fn content(&self) -> &str { &self.content } } Этот код определяет структуру Страницы с полями для заголовка и содержимого страницы, а также методы для создания новой страницы и доступа к ее заголовку и содержимому.
Далее скомпилируем код Rust в
Оглавление
GitHub - nicktretyakov/ml-wasm
ML на RUST без заморочек

t.me/oneRustnoqRust

Для чего нужна данная статья? :


- Получить представление о WebAssembly.

- Написать простой код CMS и мобильного приложения.

Узнать что такое:

  1. Полноценная нейросеть с обратным распространением ошибки
  2. Использование ndarray для матричных операций
  3. Продвинутая обработка ошибок с panic hook
  4. Система обучения с adjustable learning rate
  5. Активация через сигмоиду
  6. Поддержка многомерных входов/выходов
  7. Полная интеграция с JavaScript через WASM

Зачем Вам это уметь? :


1. Основные инструменты компиляции

  • wasm-pack
    Стандартный инструмент для сборки, тестирования и публикации Rust-кода в WASM. Генерирует JS-обвязки и интегрируется с npm.
  • wasm-bindgen
    Библиотека для взаимодействия Rust и JavaScript. Позволяет вызывать JS из Rust и наоборот, работать с DOM, Promise, файлами и т.д.
  • Целевая платформа wasm32-unknown-unknown
    Базовая компиляция Rust в WASM без зависимостей. Требует ручного управления памятью и экспортом функций.

2. Альтернативные библиотеки

  • stdweb (устаревший)
    Ранняя альтернатива wasm-bindgen для взаимодействия с JS и DOM. Сейчас рекомендуется использовать wasm-bindgen.

3. Фреймворки для веб-приложений

  • Yew
    Компонентный фреймворк с поддержкой асинхронности, виртуальным DOM и SSR (Server-Side Rendering).
  • Seed
    Фреймворк в Elm-стиле с акцентом на простоту и производительность.
  • Sycamore
    Реактивный фреймворк с компиляцией шаблонов в Rust.
  • Percy
    Инструмент для создания изоморфных веб-приложений (рендеринг на сервере и клиенте).
  • Dioxus
    Универсальный UI-фреймворк с поддержкой WASM, нативного рендеринга и SSR.

4. Интеграция с JavaScript API

  • js-sys
    Привязки к базовым объектам JavaScript (Object, Array, JSON и т.д.).
  • web-sys
    Привязки к Web API (DOM, Fetch, Canvas, WebGL и другим браузерным функциям). Используется с wasm-bindgen.

5. Специализированные сценарии

  • Emscripten (wasm32-unknown-emscripten)
    Компиляция через Emscripten для интеграции с legacy-кодом на C/C++ (редко используется в Rust).
  • Node.js + WASM
    Запуск Rust/WASM в Node.js через wasm-pack-node или кастомные настройки.

6. Ручное управление

  • Чистый WASM без библиотек
    Прямая работа с памятью и экспортом функций через #[no_mangle] и extern "C". Подходит для низкоуровневых задач.

7. Дополнительные инструменты

  • wasm-opt (из Binaryen)
    Оптимизация размера и производительности WASM-модулей.
  • wasm-bindgen-cli
    Генерация JS-привязок для ручной настройки.

CMS

Написание CMS (системы управления контентом) в Rust с WebAssembly будет включать компиляцию кода Rust в WebAssembly, а затем использование его в веб-приложении. Вот пример того, как вы можете начать структурировать простую CMS в Rust с помощью WebAssembly:

пример простой структуры Page, которая представляет страницу контента в CMS:

#[derive(Debug)]

pub struct Page {

title: String,

content: String,

}

impl Page {

pub fn new(title: String, content: String) -> Self {

Self { title, content }

}

pub fn title(&self) -> &str {

&self.title

}

pub fn content(&self) -> &str {

&self.content

}

}

Этот код определяет структуру Страницы с полями для заголовка и содержимого страницы, а также методы для создания новой страницы и доступа к ее заголовку и содержимому.
Далее скомпилируем код Rust в WebAssembly с помощью инструмента wasm-pack. При этом будет создан WASM-файл, который можно использовать в веб-приложении.
В веб-приложении можно использовать JavaScript для загрузки модуля WebAssembly и создания экземпляра структуры Page. Вот пример того, как это может выглядеть с помощью JavaScript и библиотеки wasm-bindgen:

import init from './pkg/my_cms.js';

async function run() {

const my_cms = await init();

const { Page } = my_cms;

const page = Page.new('Home', 'Welcome to our website!');

console.log(page.title(), page.content());

}

run();

Этот код использует инструкцию import для загрузки модуля WebAssembly, созданного wasm-pack. Затем он использует функцию init для инициализации модуля и получения ссылки на структуру Page. Код создает экземпляр структуры Page с помощью нового метода, а затем вызывает его методы title и content для доступа к своим данным.

Создание мобильного приложения

Создадим простую функцию на Rust, которая будет компилироваться в WebAssembly и вызвана из веб-интерфейса.

1.1 Установите необходимые инструменты:

rustup target add wasm32-unknown-unknown

cargo install wasm-bindgen-cli

1.2 Создайте новый проект на Rust:

cargo new --lib wasm_example

cd wasm_example

1.3 Напишите код на Rust:

Файл src/lib.rs:

use wasm_bindgen::prelude::*;

#[wasm_bindgen] pub fn greet(name: &str) -> String {

format!("Hello, {}!", name)

}

1.4 Скомпилируйте проект в WebAssembly:

cargo build --target wasm32-unknown-unknown --release

1.5 Генерация обвязки с помощью wasm-bindgen:

wasm-bindgen target/wasm32-unknown-unknown/release/wasm_example.wasm --out-dir ./out --target web

2.1 Использование WebView в Android:

Если вы хотите интегрировать WebAssembly код в мобильное приложение на Android, можно использовать WebView:

2.1.1 Создайте Android проект и добавьте WebView в макет:

Файл res/layout/activity_main.xml:

<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

2.1.2 В MainActivity загрузите HTML с WebAssembly:

Файл MainActivity.java или MainActivity.kt:

WebView webView = findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl("file:///android_asset/index.html");

2.1.3 Создайте файл index.html в assets:

Файл assets/index.html:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>Wasm Example</title>

<script type="module"> import init, { greet } from './wasm_example.js'; async function run() {

await init();

document.getElementById("output").textContent = greet("Android User"); }

run();

</script>

</head>

<body>

<div id="output">

</div>

<script src="wasm_example.js">

</script> </body> </html>

Когда вы скомпилируете и запустите Android приложение, оно откроет WebView, который загрузит HTML-страницу с вашим скомпилированным WebAssembly кодом на Rust. Эта страница вызовет функцию greet, которая выполнится на WebAssembly и отобразит результат.

Нейросеть с матричными операциями, компилируемой в WASM, включая JS-интерфейс

// lib.rs

#![allow(non_snake_case)]

use wasm_bindgen::prelude::*;

use ndarray::{Array2, Array1};

use rand::Rng;

use std::f64::consts::E;

#[wasm_bindgen]

pub struct NeuralNetwork {

input_size: usize,

hidden_size: usize,

output_size: usize,

weights_ih: Array2<f64>,

weights_ho: Array2<f64>,

bias_h: Array1<f64>,

bias_o: Array1<f64>,

learning_rate: f64,

}

#[wasm_bindgen]

impl NeuralNetwork {

#[wasm_bindgen(constructor)]

pub fn new(

input_size: usize,

hidden_size: usize,

output_size: usize,

learning_rate: f64,

) -> Self {

let mut rng = rand::thread_rng();

let weights_ih = Array2::from_shape_fn((hidden_size, input_size), |_| rng.gen_range(-1.0..1.0));

let weights_ho = Array2::from_shape_fn((output_size, hidden_size), |_| rng.gen_range(-1.0..1.0));

let bias_h = Array1::from_shape_fn(hidden_size, |_| rng.gen_range(-1.0..1.0));

let bias_o = Array1::from_shape_fn(output_size, |_| rng.gen_range(-1.0..1.0));

NeuralNetwork {

input_size,

hidden_size,

output_size,

weights_ih,

weights_ho,

bias_h,

bias_o,

learning_rate,

}

}

fn sigmoid(&self, x: &Array2<f64>) -> Array2<f64> {

x.mapv(|v| 1.0 / (1.0 + E.powf(-v)))

}

fn sigmoid_derivative(&self, x: &Array2<f64>) -> Array2<f64> {

x * (1.0 - x)

}

#[wasm_bindgen]

pub fn predict(&self, inputs: &[f64]) -> Vec<f64> {

let inputs = Array2::from_shape_vec((1, self.input_size), inputs.to_vec())

.expect("Invalid input shape");

let hidden = self.sigmoid(&(inputs.dot(&self.weights_ih.t()) + &self.bias_h));

let outputs = self.sigmoid(&(hidden.dot(&self.weights_ho.t()) + &self.bias_o));

outputs.into_raw_vec()

}

#[wasm_bindgen]

pub fn train(&mut self, inputs: &[f64], targets: &[f64]) {

let inputs = Array2::from_shape_vec((1, self.input_size), inputs.to_vec())

.expect("Invalid input shape");

let targets = Array2::from_shape_vec((1, self.output_size), targets.to_vec())

.expect("Invalid target shape");

// Forward pass

let hidden_inputs = inputs.dot(&self.weights_ih.t()) + &self.bias_h;

let hidden_outputs = self.sigmoid(&hidden_inputs);

let final_inputs = hidden_outputs.dot(&self.weights_ho.t()) + &self.bias_o;

let final_outputs = self.sigmoid(&final_inputs);

// Backpropagation

let output_errors = &targets - &final_outputs;

let output_gradients = output_errors * self.sigmoid_derivative(&final_outputs);

let hidden_errors = output_gradients.dot(&self.weights_ho);

let hidden_gradients = hidden_errors * self.sigmoid_derivative(&hidden_outputs);

// Update weights and biases

self.weights_ho += &(output_gradients.t().dot(&hidden_outputs) * self.learning_rate);

self.weights_ih += &(hidden_gradients.t().dot(&inputs) * self.learning_rate);

self.bias_o += &output_gradients.sum_axis(ndarray::Axis(0)) * self.learning_rate;

self.bias_h += &hidden_gradients.sum_axis(ndarray::Axis(0)) * self.learning_rate;

}

}

#[wasm_bindgen]

pub fn init_panic_hook() {

console_error_panic_hook::set_once();

}

JavaScript часть (index.js):

import init, { NeuralNetwork, init_panic_hook } from './pkg/ml_wasm.js';

async function run() {

await init();

init_panic_hook();

const nn = new NeuralNetwork(2, 4, 1, 0.5);

// XOR dataset

const training_data = [

{ inputs: [0, 0], targets: [0] },

{ inputs: [0, 1], targets: [1] },

{ inputs: [1, 0], targets: [1] },

{ inputs: [1, 1], targets: [0] },

];

// Training

for (let epoch = 0; epoch < 10000; epoch++) {

for (const data of training_data) {

nn.train(data.inputs, data.targets);

}

}

// Testing

console.log('[0,0] =>', nn.predict([0, 0])); // ≈0

console.log('[0,1] =>', nn.predict([0, 1])); // ≈1

console.log('[1,0] =>', nn.predict([1, 0])); // ≈1

console.log('[1,1] =>', nn.predict([1, 1])); // ≈0

}

run();

Сборка (Cargo.toml):

[package]

name = "ml-wasm"

version = "0.1.0"

edition = "2021"

[lib]

crate-type = ["cdylib"]

[dependencies]

wasm-bindgen = "0.2"

ndarray = "0.15"

rand = "0.8"

console_error_panic_hook = "0.1"