0
<< предыдущая заметкаследующая заметка >>
09 марта 2023
Веб-разработка: wasm, rust и yew

Сугубо технический пост, совсем для упоротых программистов. Остальным читать не советую.

Оказывается, в современных браузерах есть две возможности написать исполняемый код: это язык JavaScript и бинарный кроссплатформенный ассемблер WASM. Который по сути не язык, а результат компиляции с другого языка, например C++, GO или Rust.

Поначалу я воодушевился и даже думал, не переписать ли мне движок блога на wasm. Но оказалось, что практического смысла у wasm немного: он обычно не быстрее JS, не надежнее, не экономичнее, и уж точно не компактнее. Никаких новых фич и лазеек программисту он не открывает: как и для JS, тут действуют ровно те же политики безопасности, те же ограничения на соединения и доступ к в файловой системе. Однако есть два типа задач, где WASM будет полезен. Во-первых, это могучие вычисления, которые надо производить в браузере у пользователя. Ну, не знаю, обрабатывать мегатаблицы. Или сложными алгоритмами найти на фотке лицо и подрисовать усы и рога. Во-вторых — это использование готовых библиотек, которые почему-то (почему?) были написаны не на JS, а на C или Rust. Например, какие-то обработки изображений или процедуры блокчейна.

Про Rust пока не буду ничего говорить, надо разобраться ещё и попривыкнуть. А вот про yew (один из способов превратить Rust в wasm) скажу. Оказалось, это какой-то клон React. По крайней мере, у него подозрительно совпадает синтаксис и библиотеки. React — поделка, рожденная в недрах бесовского Фейсбука.

Но если поставлена задача собрать браузерный код на библиотеках Rust, то иного пути нет. Поэтому я пока успешно сделал модельку на yew, которая достаточно кривым способом (умнее пока не придумал) получает некие данные (строку), передает ее в подгруженный файл.wasm (240кб говна), где находится обработчик на Расте, который выдает результат:

Это транслятор чисел в кодировке Compact. Можно вводить например:
а) Число, например 12345 (результат: кодировка Scale в HEX, получается 2 байта: 0xe5c0)
б) hex-строку, начинающуюся с 0x, например: 0xe5c0 (результат: раскодированное 12345)
в) также можно просто ввести HEX-массив, начинающийся с 0X (заглавная), и получить число, где первый байт считается младшим

Смысла в этой софтинке немного, за исключением того, что это работают на Расте библиотеки hex="0.4.3" и parity-scale-codec="3.1.5", успешно превратившись в браузерный ассемблер.

Кому интересно (но в первую очередь для себя), положу код здесь:

показать

Cargo.toml

<?php[package]name="touch_wasm"
version
="0.1.0"authors= ["LLeo<lleo@lleo.me>"]
edition = "
2021"
license = "MIT OR Apache-2.0"
[dependencies]yew= {path="/home/work/RUST/web/yew-yew-v0.19.3/packages/yew" }
gloo-utils = "
0.1"
gloo = "
0.8"
js-sys = "
0.3"

hex = "0.4.3"
parity-scale-codec = "
3.1.5"

index.html

<?php<!DOCTYPE html>
<
html lang="en">

<head>

<meta charset="utf-8"/>

<title>Yew • touch_wasm</title>

<script>functionwasmt_go() {document.querySelector('#wasmt_in').innerHTML=document.querySelector('#wasmt_input').value;document.querySelector('#wasmt_button').click();
}</
script>

</head><body>

<div id="wasmt_in"style="display:none"></div>
<
div id="wasmt_buka"style="border:1px solid red;display:inline-block;position:relative;padding:10px;"></div>
<
div>
<
input id="wasmt_input"size="32"onchange="wasmt_go()">
<
input type="button"value="GO"onclick="wasmt_go()">
</
div>

</body></html>

src/main.rs

<?php// yew::set_event_bubbling(false);useyew::{prelude::*};// , html::IntoPropValueusejs_sys::Date;
use
parity_scale_codec::{Compact,Decode,Encode};// Define the possible messages which can be sent to the componentpubenumMsg{Set,
}
pub struct App{string:String,value:i64,// This will store the counter value}impl ComponentforApp{type Message=Msg;type Properties= ();

fncreate(_ctx: &Context<Self>) ->Self{Self{value:0,string:"started".to_string() }

}

fnupdate(&mut self,_ctx: &Context<Self>,msg:Self::Message) ->bool{

matchmsg{Msg::Set=> {let document=gloo_utils::document();let mut moo=document.query_selector("#wasmt_in").unwrap().unwrap().inner_html();

let str = moo.clone();

if str.starts_with("0x") {// если это SCALE HEXlet str= &str[2..];// убираем '0x'let mut bytes: &[u8] = &hex::decode(str).unwrap_or((&[0]).to_vec());// раскодируем из HEXlet scale=Compact::<u128>::decode(&mut bytes)

.unwrap_or(parity_scale_codec::Compact(0));// переведем из SCALElet r=bytes.len();// сколько осталось после выбора первого scaleifr==0{moo=format!("scale-число{} = {:}", &str,&scale);

} else {

let ost = hex::encode(bytes); // остаток неиспользованных байт

moo = format!("scale-число {} = {:} {}",

&str[0..(str.len()-ost.len())],

&scale,format!("остаток{}: {ost}",ost.len()/2)

);

}

} else if str.starts_with("0X") {// если это просто HEX без всякого SCALElet str= &str[2..];// убираем 0xlet bytes: &[u8] = &hex::decode(str).unwrap_or((&[0]).to_vec());// раскодируем из HEXlet mut bytes128: [u8;16] = Default::default();// готовим массив для u128bytes128[0..bytes.len()].copy_from_slice(&bytes);// копируем в него из нашего массива сколько надоlet value=u128::from_le_bytes(bytes128);// переводим в число u128moo=format!("hex-число{} = {}", &str, value );

} else {

// str.as_str()

let value: u128 = u128::from_str_radix(str.as_str(), 10).unwrap_or(0); // переводим в число u128

let scale = Compact(value).encode(); // переводим в Compact

let hex = hex::encode(scale); // кодируем в HEX

moo=format!("число: {} = {} scale-hex ({}): 0x{}", &str,value,hex.len()/2,hex);

}self.string=moo;// moo.to_uppercase(); // format!("[ {:} [{:}] ]", "er".to_string() , moo );self.value+=1;true// Return true to cause the displayed change to update}

}

}

fnview(&self,ctx: &Context<Self>) ->Html{let d=Date::new_0();let t=format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}",d.get_full_year(),d.get_month()+1,d.get_day(),d.get_hours(),d.get_minutes(),d.get_seconds()

);html! {

<div>{self.string.clone() }</div>// hidden div<div style="width:0;height:0;"id="wasmt_button"onclick={ctx.link().callback(|_|Msg::Set)}></div>// update time<p style="font-size:8px">{t} {" №"}{ self.value }</p>

</>}

}
}

fn main() {

let document = gloo_utils::document();

let mount_point = document.query_selector("#wasmt_buka").unwrap().unwrap();yew::start_app_in_element::<App>(mount_point);
}

А как вы думаете, какие интересные штуки можно реализовать на wasm вместо JS?


UPD: Подумав, удалил все комменты про React, он вообще не тема сегодняшней заметки, а был упомянут лишь в контексте, что на него похож yew, на котором я делал wasm, что мне не сильно понравилось. Откуда разгорелся такой флейм и кому он нужен — загадка. Пойду удалю все комменты на эту тему, включая свои. Приношу извинения всем, кого так больно ранит мое мнение про React, Facebook, Windows и ряд других религиозных святынь. Это мое личное мнение, мой собственный осознанный выбор между альтернативами, я время от времени высказываю свое мнение в своем личном дневнике, но никому не навязываю. Просто разрешите мне иметь собственное мнение, не надо так оскорбляться.

<< предыдущая заметка следующая заметка >>
пожаловаться на эту публикацию администрации портала
архив понравившихся мне ссылок

Комментарии к этой заметке скрываются - они будут видны только вам и мне.

Оставить комментарий