rust 编译wasm 使用

创建rust lib 项目

cargo new --lib rust_wasm

修改Cargo.toml

cdylib 是动态库, 可以编译多个跨平台动态库(rlib 是给rust 代码内部用的,例如你写了个算法但是不想暴露算法具体规则只想给别人用,就可以使用rlib 了)

复制代码
[lib]
crate-type = ["cdylib"]

依赖库

复制代码
cargo add wasm-bindge

lib.rs rust 代码

例如 暴露方法就使用 #[wasm_bindgen],具体用法看官网文档

c 复制代码
use byteorder::{BigEndian, ByteOrder};
use serde::{Deserialize, Serialize};
use serde_json::{self, Value};
use std::io::{Cursor, Read};
use wasm_bindgen::prelude::*;
use serde_wasm_bindgen::{to_value, from_value};
use js_sys::Uint8Array;
use web_sys; // 引入 console 模块

#[wasm_bindgen]
pub fn fib(n: u32) -> u32 {
   if n <= 1 {
      n
   } else {
      fib(n - 1) + fib(n - 2)
   }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct DataPacket {
   msg_id: u32,
   protocol: u8,
   body: Value,
}

#[wasm_bindgen]
pub fn pack(msg_id: u32, protocol: u8, body: JsValue) -> Result<Box<[u8]>, JsValue> {
   let body: Value = from_value(body).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let packet = DataPacket { msg_id, protocol, body };

   let json_bytes = serde_json::to_vec(&packet.body).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let json_length = json_bytes.len() as u32;

   let mut buffer = Vec::with_capacity(4 + 4 + 1 + json_bytes.len());

   let mut msg_id_buf = [0u8; 4];
   BigEndian::write_u32(&mut msg_id_buf, packet.msg_id);
   buffer.extend_from_slice(&msg_id_buf);

   let mut length_buf = [0u8; 4];
   BigEndian::write_u32(&mut length_buf, json_length);
   buffer.extend_from_slice(&length_buf);

   buffer.push(packet.protocol);
   buffer.extend_from_slice(&json_bytes);

   Ok(buffer.into_boxed_slice())
}

#[wasm_bindgen]
pub fn unpack(data: &[u8]) -> Result<JsValue, JsValue> {
   let mut cursor = Cursor::new(data);

   let mut msg_id_buf = [0u8; 4];
   cursor.read_exact(&mut msg_id_buf).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let msg_id = BigEndian::read_u32(&msg_id_buf);

   let mut length_buf = [0u8; 4];
   cursor.read_exact(&mut length_buf).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let json_length = BigEndian::read_u32(&length_buf) as usize;

   let mut protocol_buf = [0u8; 1];
   cursor.read_exact(&mut protocol_buf).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let protocol = protocol_buf[0];

   let mut json_bytes = vec![0u8; json_length];
   cursor.read_exact(&mut json_bytes).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let body: Value = serde_json::from_slice(&json_bytes).map_err(|err| JsValue::from_str(&err.to_string()))?;

   let packet = DataPacket { msg_id, protocol, body };
   to_value(&packet).map_err(|err| JsValue::from_str(&err.to_string()))
}


#[wasm_bindgen]
pub fn process_bytes(data: Uint8Array) -> String {
   // 获取 Uint8Array 的长度
   let data_len = data.length() as usize;
   let mut bytes = Vec::with_capacity(data_len);

   // 将 Uint8Array 的内容复制到 Rust 向量中
   unsafe {
      bytes.set_len(data_len);
      data.copy_to(&mut bytes);
   }

   // 将字节数组转换为字符串
   match String::from_utf8(bytes) {
      Ok(s) => s,
      Err(e) => {
         web_sys::console::error_1(&format!("转换字节到字符串失败: {}", e).into());
         String::from("转换失败")
      },
   }
}

介绍两种打包方式一种就是cargo 打包,第二种 wasm-pack 工具打包

1.cargo 打包安装目标平台,可以通过命令查看 rust 覆盖的平台,还是很强的基本覆盖所有平台,命令查看方式 rustup target list

sh 复制代码
rustup target add wasm32-unknown-unknown
rustup show 确认是否安装成功 或者  rustup target list --installed 
cargo build --target wasm32-unknown-unknown
# 生成 target/debug 有.wasm 后缀

为了让js直接调用,由于我们使用了 wasm_bindgen 所以也要安装对应的插件,把js封装让开发人员直接调用

复制代码
cargo install wasm-bindgen-cli
cargo install --list 查看
然后使用命令
wasm-bindgen target/wasm32-unknown-unknown/debug/rust_wasm.wasm --out-dir ./out --target web
这个web 是对应的平台使用方式,out 就是js目录

bundler:编译成给webpack之类的脚手架使用
web:编译成web可直接使用
nodejs:编译成可通过require来加载的node模块
deno:编译成可通过import加载的deno模块
no-modules:跟web类似,但是更旧,且不能使用es模块
  1. 直接使用其wasm-pack 更好,支持ts,更多的其他版本

    cargo install wasm-pack
    wasm-pack build --target web # 这个直接生成了pkg目录

    bundler:编译成给webpack之类的脚手架使用
    web:编译成web可直接使用
    nodejs:编译成可通过require来加载的node模块
    deno:编译成可通过import加载的deno模块
    no-modules:跟web类似,但是更旧,且不能使用es模块

现在演示下使用web 版本的wasm

在项目pkg 同一级创建一个index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>使用Rust和WebAssembly</title>
</head>
<body>
Hello, World!
</body>
<script type="module">
    import init, { pack,unpack } from './pkg/rust_wasm.js';

    const run = async () => {
        await init();  // 指定远程的 .wasm 文件
          const result = fib(10);
        console.log("result:",result);
        // 示例: 调用 pack 函数
        const packet = pack(1, 1, JSON.stringify({ u: 3333 }));
        console.log('Packed data:', packet);

        // 示例: 调用 unpack 函数
        const unpackedData = unpack(packet);
        console.log('Unpacked data:', unpackedData);}

    run();
</script>
</html>

由于wasm 不能是加载本地文件方式,所以需要启动个http服务

复制代码
在.index.html 和pkg 和 index.html 同一级,访问域名看终端输出看终端输出
python3 -m http.server
默认是 8000 端口, 访问 http://127.0.0.1:8000/ ,

如果客户端项目node 和这前端项目支持 webpack 打包的可以打出对应的 wasm

参考链接:
https://github.com/hunter-ji/Blog/issues/72#issue-1756474747

https://juejin.cn/post/7156102433581383716

相关推荐
DongLi012 天前
rustlings 学习笔记 -- exercises/05_vecs
rust
番茄灭世神3 天前
Rust学习笔记第2篇
rust·编程语言
shimly1234563 天前
(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(19) Option
rust
@atweiwei3 天前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
shimly1234563 天前
(done) 速通 rustlings(24) 错误处理2 --- 涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(23) 特性 Traits
rust
shimly1234563 天前
(done) 速通 rustlings(17) 哈希表
rust
shimly1234563 天前
(done) 速通 rustlings(15) 字符串
rust
shimly1234563 天前
(done) 速通 rustlings(22) 泛型
rust